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

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

このコミットは、Go言語の標準ライブラリosパッケージ内のFileMode型のString()メソッドにおけるバッファサイズの問題を修正するものです。具体的には、FileModeのビット表現を文字列に変換する際に使用される内部バッファのサイズが不足していたため、これを適切なサイズに拡張しています。これにより、FileModeの全てのビット情報が正しく文字列として表現されるようになります。

コミット

  • コミットハッシュ: 2dfcbd0a3b216a793f3a450889e51781c142539b
  • 作者: Stefan Nilsson snilsson@nada.kth.se
  • コミット日時: 2012年3月24日 土曜日 08:16:57 +1100

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

https://github.com/golang/go/commit/2dfcbd0a3b216a793f3a450889e51781c142539b

元コミット内容

os: add missing byte to FileMode buffer

32 bytes is enough for all FileMode bits.

R=golang-dev, r
CC=golang-dev
https://golang.org/cl/5853044

変更の背景

Go言語のosパッケージには、ファイルやディレクトリのパーミッション、種類(ディレクトリ、シンボリックリンクなど)を表すFileMode型が存在します。この型はuint32のエイリアスであり、32ビットの情報を保持できます。

FileMode型には、その値を人間が読める形式の文字列に変換するためのString()メソッドが実装されています。このメソッドの内部では、変換された文字列を一時的に保持するためのバイト配列(バッファ)が使用されていました。

元の実装では、このバッファのサイズが[20]byte、つまり20バイトに設定されていました。しかし、FileModeuint32であり、その全てのビットが特定のフラグ(パーミッションやファイルタイプ)を表す可能性があるため、全てのビットがセットされた場合に生成される文字列の長さが20バイトを超える可能性がありました。特に、FileModeの各ビットがString()メソッド内で定義されているstr定数("dalTLDpSugct")の文字に対応しており、これらの文字がバッファに追加されるロジックを考慮すると、20バイトでは不足するケースが発生し得ました。

このバッファサイズの不足は、FileModeの特定のビットパターンが正しく文字列化されない、あるいはバッファオーバーフローのような予期せぬ動作を引き起こす可能性がありました。このコミットは、この潜在的な問題を解決し、FileModeの全ての可能な値が正確に文字列として表現されることを保証するために行われました。

前提知識の解説

os.FileMode

os.FileModeはGo言語のosパッケージで定義されている型で、ファイルやディレクトリのモードビットを表します。これには、Unixライクなシステムにおけるファイルパーミッション(読み取り、書き込み、実行権限)や、ファイルの種類(通常ファイル、ディレクトリ、シンボリックリンク、デバイスファイルなど)に関する情報が含まれます。内部的にはuint32型として実装されており、32ビットの情報を保持できます。

String()メソッド

Go言語では、任意の型に対してString() stringメソッドを定義することで、その型の値を文字列として表現する方法をカスタマイズできます。これはfmtパッケージの関数(例: fmt.Println)が値を表示する際に自動的に呼び出すため、デバッグ出力やログ出力において非常に便利です。FileModeString()メソッドは、FileModeのビットフラグを解析し、対応する文字(例: d for directory, L for symlink, r for read permission)を組み合わせて人間が読める形式の文字列を生成します。

バイト配列とバッファ

プログラムにおいて、データを一時的に格納するために固定長のメモリ領域が確保されることがあります。これをバッファと呼びます。Go言語では、[N]byteのように宣言することで、Nバイトの固定長バイト配列を作成できます。String()メソッドのような文字列変換処理では、変換結果の文字列をこのバッファに書き込み、最終的にそのバッファの内容を基に文字列を構築します。バッファのサイズが不足すると、書き込むべきデータがバッファに収まらず、データが切り捨てられたり、メモリ破壊を引き起こしたりする可能性があります。

uint32

uint32は、符号なし32ビット整数型です。これは0から2^32-1までの値を表現できます。32ビットは4バイトに相当します。FileModeuint32であるということは、最大で32個の独立したフラグ(ビット)を持つことができることを意味します。

技術的詳細

このコミットの技術的な核心は、FileModeString()メソッドが、FileModeの32ビット全ての情報を文字列として表現するために必要なバッファサイズを正確に計算し、それを確保することにあります。

元のコードでは、var buf [20]byteとして20バイトのバッファを確保していました。FileModeString()メソッドのロジックを見ると、const str = "dalTLDpSugct"という12文字の定数が定義されており、FileModeの各ビットがセットされているかどうかに応じて、このstr定数から対応する文字をbufに書き込んでいます。さらに、パーミッションビット(rwxrwxrwx)も文字列として追加されます。

FileModeuint32であり、最大で32個の異なるビットフラグを持つ可能性があります。String()メソッドの内部ロジックでは、これらのビットをループでチェックし、対応する文字をバッファに追加していきます。例えば、str定数に含まれる12個の特殊なファイルモードビット(ディレクトリ、シンボリックリンクなど)と、9個のパーミッションビット(ユーザー、グループ、その他の読み取り/書き込み/実行)を考慮すると、合計で最大21文字程度の情報が文字列として表現される可能性があります。しかし、これに加えて、FileModeにはModeSetuid, ModeSetgid, ModeStickyなどの特殊なパーグミッションビットも存在し、これらも文字列に変換される可能性があります。

コミットメッセージにある「32 bytes is enough for all FileMode bits.」という記述は、FileModeuint32(32ビット)であることを直接示唆しており、最悪の場合、全てのビットが何らかの形で文字列表現に寄与する可能性を考慮して、32バイトのバッファが必要であると判断されたことを意味します。これは、各ビットが1文字に対応するような単純なマッピングではないにしても、安全側に倒した適切なバッファサイズであると考えられます。

この変更により、FileModeのどのようなビットパターンであっても、String()メソッドが正しく、かつ安全にその文字列表現を生成できるようになります。

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

変更はsrc/pkg/os/types.goファイルの一箇所のみです。

--- a/src/pkg/os/types.go
+++ b/src/pkg/os/types.go
@@ -58,7 +58,7 @@ const (
 
 func (m FileMode) String() string {
 	const str = "dalTLDpSugct"
-	var buf [20]byte
+	var buf [32]byte // Mode is uint32.
 	w := 0
 	for i, c := range str {
 		if m&(1<<uint(32-1-i)) != 0 {

コアとなるコードの解説

変更された行は以下の通りです。

-	var buf [20]byte
+	var buf [32]byte // Mode is uint32.

この変更は、FileMode型のString()メソッド内で使用されるローカル変数bufの宣言を変更しています。

  • 変更前: var buf [20]byte

    • これは、20バイトの固定長バイト配列bufを宣言し、初期化しています。この配列が、FileModeの文字列表現を構築するための一時的なバッファとして使用されていました。
  • 変更後: var buf [32]byte // Mode is uint32.

    • バッファのサイズが20バイトから32バイトに拡張されました。
    • 追加されたコメント// Mode is uint32.は、この変更の理由を明確に示しています。FileModeuint32型であるため、その全てのビット(32ビット)を文字列として表現する際に、最大で32文字分のスペースが必要になる可能性があることを示唆しています。これにより、FileModeの全ての可能なビットパターンが、バッファオーバーフローの心配なく、正しく文字列に変換されることが保証されます。

この修正は、一見すると小さな変更ですが、FileModeString()メソッドの堅牢性と正確性を向上させる上で重要です。

関連リンク

参考にした情報源リンク