[インデックス 13346] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるWindows固有のcopyFindData
関数のバグ修正に関するものです。具体的には、src/pkg/syscall/ztypes_windows.go
ファイル内のcopyFindData
関数が修正されています。この関数は、Windows APIのWIN32_FIND_DATA
構造体に関連するデータをコピーする際に、文字列の終端処理に関する誤った仮定を修正しています。
コミット
commit 1c4e20744a48ee0d7cdb74ed1cab5196345cf6a2
Author: Russ Cox <rsc@golang.org>
Date: Wed Jun 13 16:44:19 2012 -0400
syscall: fix windows copyFindData
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6301076
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1c4e20744a48ee0d7cdb74ed1cab5196345cf6a2
元コミット内容
syscall: fix windows copyFindData
変更の背景
この変更は、Go言語のsyscall
パッケージがWindowsのファイル検索APIであるFindFirstFile
/FindNextFile
などから返されるWIN32_FIND_DATA
構造体を扱う際に発生していた潜在的なバグを修正するために行われました。
WIN32_FIND_DATA
構造体には、ファイル名と代替ファイル名(8.3形式の短いファイル名)を格納するための固定長配列cFileName
とcAlternateFileName
が含まれています。これらのフィールドは、C言語の文字列と同様にヌル終端されることが期待されます。
Goのsyscall
パッケージでは、Windows APIから受け取った生の構造体(win32finddata1
など、内部表現)を、Goのユーザーが扱いやすいようにラップした構造体(Win32finddata
)に変換するcopyFindData
関数が存在します。
元の実装では、src
(生の構造体)のFileName
フィールドがdst
(ラップされた構造体)のFileName
フィールドよりも1要素短いと誤解されており、dst.FileName
の最後の要素を明示的にゼロ(ヌル)に設定していました。同様に、src.AlternateFileName
についても誤ったヌル終端処理が行われていました。
この誤解により、ヌル終端が正しく行われない、または余分なゼロが書き込まれる可能性があり、ファイル名が正しく処理されない、あるいはバッファオーバーフローのような問題を引き起こす可能性がありました。このコミットは、この誤ったヌル終端処理の仮定を修正し、Windows APIの仕様に合致させることを目的としています。
前提知識の解説
1. Windows API WIN32_FIND_DATA
構造体
WIN32_FIND_DATA
は、Windowsのファイル検索関数(FindFirstFile
, FindNextFile
など)がファイルやディレクトリの情報を返すために使用する構造体です。この構造体には、以下のような重要なフィールドが含まれます。
dwFileAttributes
: ファイルの属性(読み取り専用、ディレクトリなど)。ftCreationTime
,ftLastAccessTime
,ftLastWriteTime
: ファイルの作成、最終アクセス、最終書き込み日時。nFileSizeHigh
,nFileSizeLow
: ファイルサイズ。cFileName[MAX_PATH]
: ファイルのロングファイル名。MAX_PATH
は通常260文字。cAlternateFileName[14]
: ファイルの8.3形式の短いファイル名。
cFileName
とcAlternateFileName
は、固定長のWCHAR
(ワイド文字、UTF-16)配列であり、C言語の文字列と同様にヌル終端(\0
)されることが期待されます。つまり、文字列の実際の長さは、最初のヌル文字までのバイト数で決まります。
2. Go言語のsyscall
パッケージ
Go言語のsyscall
パッケージは、オペレーティングシステムが提供する低レベルのプリミティブ(システムコール)へのインターフェースを提供します。これにより、GoプログラムからOS固有の機能(ファイル操作、ネットワーク、プロセス管理など)を直接呼び出すことができます。
Windowsの場合、syscall
パッケージはWin32 APIの関数や構造体をGoの型にマッピングし、Goプログラムからそれらを呼び出せるようにします。このマッピングには、C言語の構造体とGoの構造体の間のデータコピーや変換が含まれることがよくあります。
3. 固定長配列とヌル終端文字列
C言語では、文字列は通常、文字の配列として表現され、文字列の終わりを示すためにヌル文字(\0
)が使用されます。WIN32_FIND_DATA
構造体内のcFileName
やcAlternateFileName
のようなフィールドは、固定長の配列ですが、その中に格納される文字列はヌル終端される必要があります。これは、配列のサイズが文字列の最大長を定義する一方で、実際の文字列の長さはヌル文字の位置によって決まることを意味します。
Goのcopy
関数は、スライス間で要素をコピーしますが、ヌル終端の概念は持ちません。そのため、C言語のヌル終端文字列をGoのバイトスライスや文字列に変換する際には、ヌル文字の処理を明示的に行う必要があります。
技術的詳細
このコミットの技術的な核心は、Goのsyscall
パッケージがWindows APIのWIN32_FIND_DATA
構造体内の文字列フィールド(cFileName
とcAlternateFileName
)をGoの内部構造体にコピーする際の、ヌル終端に関する正確な理解と実装にあります。
元のコードでは、Win32finddata
(Goの公開構造体)とwin32finddata1
(Windows APIの内部構造体に対応するGoの型)の間でFileName
とAlternateFileName
をコピーする際に、以下のようなコメントと処理がありました。
// The src is 1 element shorter than dst. Zero that last one.
copy(dst.FileName[:], src.FileName[:])
dst.FileName[len(dst.FileName)-1] = 0 // dstの最後の要素をゼロにする
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
src.AlternateFileName[len(dst.AlternateFileName)-1] = 0 // srcの最後の要素をゼロにする (これは誤り)
このコメントとコードは、src
(win32finddata1
のフィールド)がdst
(Win32finddata
のフィールド)よりも1要素短いという誤った仮定に基づいていました。そして、その仮定に基づいて、dst.FileName
の最後の要素を明示的にゼロに設定していました。さらに、src.AlternateFileName
の最後の要素をゼロに設定するという、より深刻な誤りも含まれていました。これは、src
が読み取り元であるにもかかわらず、その内容を書き換えるという問題を引き起こす可能性がありました。
実際のところ、Windows APIのWIN32_FIND_DATA
構造体内のcFileName
とcAlternateFileName
は、Goのwin32finddata1
構造体で表現される際に、ヌル終端文字を含む形で定義されています。つまり、Goのcopy
関数でsrc.FileName[:]
からdst.FileName[:]
へコピーするだけで、ヌル終端文字も正しくコピーされるはずです。
このコミットは、この誤解を修正し、src
とdst
の配列サイズが実際には同じか、src
がdst
よりも大きい(ただし、ヌル終端されているため問題ない)という正しい理解に基づいています。したがって、明示的なヌル終端処理は不要であり、むしろ誤った動作を引き起こす可能性がありました。
修正後のコードでは、コメントが以下のように変更され、明示的なヌル終端処理の行が削除されました。
// The src is 1 element bigger than dst, but it must be NUL.
copy(dst.FileName[:], src.FileName[:])
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
この変更は、copy
関数がヌル終端文字を含むすべてのバイトを正しくコピーすることを信頼し、余分な操作を削除することで、コードをより正確で堅牢なものにしています。これにより、Windowsのファイル名がGoプログラムで正しく扱われるようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/ztypes_windows.go
ファイル内のcopyFindData
関数に集中しています。
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -376,11 +376,9 @@ func copyFindData(dst *Win32finddata, src *win32finddata1) {
dst.Reserved0 = src.Reserved0
dst.Reserved1 = src.Reserved1
- // The src is 1 element shorter than dst. Zero that last one.
+ // The src is 1 element bigger than dst, but it must be NUL.
copy(dst.FileName[:], src.FileName[:])
- dst.FileName[len(dst.FileName)-1] = 0
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
- src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
}
具体的には、以下の2行が削除されました。
dst.FileName[len(dst.FileName)-1] = 0
src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
そして、コメントが変更されました。
コアとなるコードの解説
copyFindData
関数は、Windows APIから直接取得したwin32finddata1
型のデータ(src
)を、Goのアプリケーション層で利用しやすいWin32finddata
型のデータ(dst
)に変換する役割を担っています。
削除された2行は、それぞれFileName
とAlternateFileName
という文字列フィールドのヌル終端を明示的に行おうとしていました。
dst.FileName[len(dst.FileName)-1] = 0
: これは、dst.FileName
の最後の要素をゼロに設定することで、ヌル終端を保証しようとするものでした。しかし、copy(dst.FileName[:], src.FileName[:])
が正しくヌル終端文字をコピーしていれば、この行は不要であり、場合によっては誤った位置にヌルを書き込む可能性がありました。src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
: これはさらに問題で、読み取り元であるsrc
のデータを変更しようとしていました。これは、src
が不変であるべきという原則に反し、予期せぬ副作用を引き起こす可能性がありました。
新しいコメント// The src is 1 element bigger than dst, but it must be NUL.
は、win32finddata1
のフィールドがWin32finddata
のフィールドよりもサイズが大きい場合があるが、それはヌル終端文字を含むためであり、copy
関数が正しく機能することを意味しています。つまり、copy
関数がヌル終端文字を含めてすべての関連バイトをコピーするため、明示的なヌル終端処理は不要であり、むしろ誤った動作を引き起こす可能性があったということです。
この修正により、copyFindData
関数は、Windows APIのWIN32_FIND_DATA
構造体からGoの構造体へのデータコピーを、より正確かつ安全に行うことができるようになりました。
関連リンク
参考にした情報源リンク
- 上記のGitHubコミットページ
- Go言語の公式ドキュメント
- Microsoft LearnのWindows APIドキュメント
- Go言語の
syscall
パッケージのソースコード(特にztypes_windows.go
) - Go言語のコードレビュープロセスに関する一般的な知識
- C言語における文字列とヌル終端の概念に関する一般的な知識
- Go言語の
copy
関数の動作に関する一般的な知識 - Go言語の
syscall
パッケージの歴史的な変更履歴(git blame
など) - Go言語のIssueトラッカーやメーリングリスト(
golang.org/cl/6301076
が参照しているGoのコードレビューシステム)# [インデックス 13346] ファイルの概要
このコミットは、Go言語のsyscall
パッケージにおけるWindows固有のcopyFindData
関数のバグ修正に関するものです。具体的には、src/pkg/syscall/ztypes_windows.go
ファイル内のcopyFindData
関数が修正されています。この関数は、Windows APIのWIN32_FIND_DATA
構造体に関連するデータをコピーする際に、文字列の終端処理に関する誤った仮定を修正しています。
コミット
commit 1c4e20744a48ee0d7cdb74ed1cab5196345cf6a2
Author: Russ Cox <rsc@golang.org>
Date: Wed Jun 13 16:44:19 2012 -0400
syscall: fix windows copyFindData
R=golang-dev, bradfitz
CC=golang-dev
https://golang.org/cl/6301076
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/1c4e20744a48ee0d7cdb74ed1cab5196345cf6a2
元コミット内容
syscall: fix windows copyFindData
変更の背景
この変更は、Go言語のsyscall
パッケージがWindowsのファイル検索APIであるFindFirstFile
/FindNextFile
などから返されるWIN32_FIND_DATA
構造体を扱う際に発生していた潜在的なバグを修正するために行われました。
WIN32_FIND_DATA
構造体には、ファイル名と代替ファイル名(8.3形式の短いファイル名)を格納するための固定長配列cFileName
とcAlternateFileName
が含まれています。これらのフィールドは、C言語の文字列と同様にヌル終端されることが期待されます。
Goのsyscall
パッケージでは、Windows APIから受け取った生の構造体(win32finddata1
など、内部表現)を、Goのユーザーが扱いやすいようにラップした構造体(Win32finddata
)に変換するcopyFindData
関数が存在します。
元の実装では、src
(生の構造体)のFileName
フィールドがdst
(ラップされた構造体)のFileName
フィールドよりも1要素短いと誤解されており、dst.FileName
の最後の要素を明示的にゼロ(ヌル)に設定していました。同様に、src.AlternateFileName
についても誤ったヌル終端処理が行われていました。
この誤解により、ヌル終端が正しく行われない、または余分なゼロが書き込まれる可能性があり、ファイル名が正しく処理されない、あるいはバッファオーバーフローのような問題を引き起こす可能性がありました。このコミットは、この誤ったヌル終端処理の仮定を修正し、Windows APIの仕様に合致させることを目的としています。
前提知識の解説
1. Windows API WIN32_FIND_DATA
構造体
WIN32_FIND_DATA
は、Windowsのファイル検索関数(FindFirstFile
, FindNextFile
など)がファイルやディレクトリの情報を返すために使用する構造体です。この構造体には、以下のような重要なフィールドが含まれます。
dwFileAttributes
: ファイルの属性(読み取り専用、ディレクトリなど)。ftCreationTime
,ftLastAccessTime
,ftLastWriteTime
: ファイルの作成、最終アクセス、最終書き込み日時。nFileSizeHigh
,nFileSizeLow
: ファイルサイズ。cFileName[MAX_PATH]
: ファイルのロングファイル名。MAX_PATH
は通常260文字。cAlternateFileName[14]
: ファイルの8.3形式の短いファイル名。
cFileName
とcAlternateFileName
は、固定長のWCHAR
(ワイド文字、UTF-16)配列であり、C言語の文字列と同様にヌル終端(\0
)されることが期待されます。つまり、文字列の実際の長さは、最初のヌル文字までのバイト数で決まります。
2. Go言語のsyscall
パッケージ
Go言語のsyscall
パッケージは、オペレーティングシステムが提供する低レベルのプリミティブ(システムコール)へのインターフェースを提供します。これにより、GoプログラムからOS固有の機能(ファイル操作、ネットワーク、プロセス管理など)を直接呼び出すことができます。
Windowsの場合、syscall
パッケージはWin32 APIの関数や構造体をGoの型にマッピングし、Goプログラムからそれらを呼び出せるようにします。このマッピングには、C言語の構造体とGoの構造体の間のデータコピーや変換が含まれることがよくあります。
3. 固定長配列とヌル終端文字列
C言語では、文字列は通常、文字の配列として表現され、文字列の終わりを示すためにヌル文字(\0
)が使用されます。WIN32_FIND_DATA
構造体内のcFileName
やcAlternateFileName
のようなフィールドは、固定長の配列ですが、その中に格納される文字列はヌル終端される必要があります。これは、配列のサイズが文字列の最大長を定義する一方で、実際の文字列の長さはヌル文字の位置によって決まることを意味します。
Goのcopy
関数は、スライス間で要素をコピーしますが、ヌル終端の概念は持ちません。そのため、C言語のヌル終端文字列をGoのバイトスライスや文字列に変換する際には、ヌル文字の処理を明示的に行う必要があります。
技術的詳細
このコミットの技術的な核心は、Goのsyscall
パッケージがWindows APIのWIN32_FIND_DATA
構造体内の文字列フィールド(cFileName
とcAlternateFileName
)をGoの内部構造体にコピーする際の、ヌル終端に関する正確な理解と実装にあります。
元のコードでは、Win32finddata
(Goの公開構造体)とwin32finddata1
(Windows APIの内部構造体に対応するGoの型)の間でFileName
とAlternateFileName
をコピーする際に、以下のようなコメントと処理がありました。
// The src is 1 element shorter than dst. Zero that last one.
copy(dst.FileName[:], src.FileName[:])
dst.FileName[len(dst.FileName)-1] = 0 // dstの最後の要素をゼロにする
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
src.AlternateFileName[len(dst.AlternateFileName)-1] = 0 // srcの最後の要素をゼロにする (これは誤り)
このコメントとコードは、src
(win32finddata1
のフィールド)がdst
(Win32finddata
のフィールド)よりも1要素短いという誤った仮定に基づいていました。そして、その仮定に基づいて、dst.FileName
の最後の要素を明示的にゼロに設定していました。さらに、src.AlternateFileName
の最後の要素をゼロに設定するという、より深刻な誤りも含まれていました。これは、src
が読み取り元であるにもかかわらず、その内容を書き換えるという問題を引き起こす可能性がありました。
実際のところ、Windows APIのWIN32_FIND_DATA
構造体内のcFileName
とcAlternateFileName
は、Goのwin32finddata1
構造体で表現される際に、ヌル終端文字を含む形で定義されています。つまり、Goのcopy
関数でsrc.FileName[:]
からdst.FileName[:]
へコピーするだけで、ヌル終端文字も正しくコピーされるはずです。
このコミットは、この誤解を修正し、src
とdst
の配列サイズが実際には同じか、src
がdst
よりも大きい(ただし、ヌル終端されているため問題ない)という正しい理解に基づいています。したがって、明示的なヌル終端処理は不要であり、むしろ誤った動作を引き起こす可能性がありました。
修正後のコードでは、コメントが以下のように変更され、明示的なヌl終端処理の行が削除されました。
// The src is 1 element bigger than dst, but it must be NUL.
copy(dst.FileName[:], src.FileName[:])
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
この変更は、copy
関数がヌル終端文字を含むすべてのバイトを正しくコピーすることを信頼し、余分な操作を削除することで、コードをより正確で堅牢なものにしています。これにより、Windowsのファイル名がGoプログラムで正しく扱われるようになります。
コアとなるコードの変更箇所
変更はsrc/pkg/syscall/ztypes_windows.go
ファイル内のcopyFindData
関数に集中しています。
--- a/src/pkg/syscall/ztypes_windows.go
+++ b/src/pkg/syscall/ztypes_windows.go
@@ -376,11 +376,9 @@ func copyFindData(dst *Win32finddata, src *win32finddata1) {
dst.Reserved0 = src.Reserved0
dst.Reserved1 = src.Reserved1
- // The src is 1 element shorter than dst. Zero that last one.
+ // The src is 1 element bigger than dst, but it must be NUL.
copy(dst.FileName[:], src.FileName[:])
- dst.FileName[len(dst.FileName)-1] = 0
copy(dst.AlternateFileName[:], src.AlternateFileName[:])
- src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
}
具体的には、以下の2行が削除されました。
dst.FileName[len(dst.FileName)-1] = 0
src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
そして、コメントが変更されました。
コアとなるコードの解説
copyFindData
関数は、Windows APIから直接取得したwin32finddata1
型のデータ(src
)を、Goのアプリケーション層で利用しやすいWin32finddata
型のデータ(dst
)に変換する役割を担っています。
削除された2行は、それぞれFileName
とAlternateFileName
という文字列フィールドのヌル終端を明示的に行おうとしていました。
dst.FileName[len(dst.FileName)-1] = 0
: これは、dst.FileName
の最後の要素をゼロに設定することで、ヌル終端を保証しようとするものでした。しかし、copy(dst.FileName[:], src.FileName[:])
が正しくヌル終端文字をコピーしていれば、この行は不要であり、場合によっては誤った位置にヌルを書き込む可能性がありました。src.AlternateFileName[len(dst.AlternateFileName)-1] = 0
: これはさらに問題で、読み取り元であるsrc
のデータを変更しようとしていました。これは、src
が不変であるべきという原則に反し、予期せぬ副作用を引き起こす可能性がありました。
新しいコメント// The src is 1 element bigger than dst, but it must be NUL.
は、win32finddata1
のフィールドがWin32finddata
のフィールドよりもサイズが大きい場合があるが、それはヌル終端文字を含むためであり、copy
関数が正しく機能することを意味しています。つまり、copy
関数がヌル終端文字を含めてすべての関連バイトをコピーするため、明示的なヌル終端処理は不要であり、むしろ誤った動作を引き起こす可能性があったということです。
この修正により、copyFindData
関数は、Windows APIのWIN32_FIND_DATA
構造体からGoの構造体へのデータコピーを、より正確かつ安全に行うことができるようになりました。
関連リンク
参考にした情報源リンク
- 上記のGitHubコミットページ
- Go言語の公式ドキュメント
- Microsoft LearnのWindows APIドキュメント
- Go言語の
syscall
パッケージのソースコード(特にztypes_windows.go
) - Go言語のコードレビュープロセスに関する一般的な知識
- C言語における文字列とヌル終端の概念に関する一般的な知識
- Go言語の
copy
関数の動作に関する一般的な知識 - Go言語のIssueトラッカーやメーリングリスト(
golang.org/cl/6301076
が参照しているGoのコードレビューシステム)