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

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

このコミットは、Go言語の syscall パッケージにおけるOpenBSD固有のバグ修正に関するものです。具体的には、OpenBSD 5.0以前のバージョンで sysctl システムコールが kern.hostname および kern.domainname の長さを正しく返さない問題に対するワークアラウンドの適用条件を修正しています。

コミット

  • コミットハッシュ: 595efd0d205b2a1fe143440088f8f394b09c3b8c
  • Author: Joel Sing jsing@google.com
  • Date: Thu Dec 1 10:17:33 2011 +1100
  • コミットメッセージ:
    syscall: fix openbsd sysctl hostname/domainname workaround
    
    Fixes #2509.
    
    R=golang-dev, adg
    CC=golang-dev
    https://golang.org/cl/5451055
    

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

https://github.com/golang/go/commit/595efd0d205b2a1fe143440088f8f394b09c3b8c

元コミット内容

syscall: fix openbsd sysctl hostname/domainname workaround

Fixes #2509.

R=golang-dev, adg
CC=golang-dev
https://golang.org/cl/5451055

変更の背景

このコミットは、Go言語の syscall パッケージがOpenBSD上で sysctl システムコールを使用してホスト名 (kern.hostname) やドメイン名 (kern.domainname) を取得する際に発生していた問題を修正するために行われました。

OpenBSD 5.0より前のバージョンには、sysctl システムコールに関する特定のバグが存在しました。このバグは、sysctl を呼び出す際に、取得するデータのバッファサイズを問い合わせるために oldp 引数に nil を渡した場合に、kern.hostnamekern.domainname といった特定のカーネルパラメータの長さが常に 0 として返されるというものでした。これにより、Goの syscall パッケージは、これらの情報を取得するために必要なバッファのサイズを正しく判断できず、結果としてホスト名やドメイン名の取得に失敗する可能性がありました。

Goの syscall パッケージには、このOpenBSDのバグに対するワークアラウンドが既に実装されていましたが、そのワークアラウンドが適用される条件に誤りがありました。具体的には、Sysctl 関数の引数として渡された name (問い合わせるカーネルパラメータの名前) ではなく、Sysctl 関数が内部で処理した結果として得られる value (取得された値、またはエラー発生時のダミー値) を参照してワークアラウンドを適用しようとしていました。この誤った条件判定により、ワークアラウンドが意図した通りに機能せず、問題が解決されないままでした。

この問題はGoのIssue #2509として報告されており、今回のコミットはその問題を解決するためのものです。

前提知識の解説

sysctl

sysctl は、Unix系オペレーティングシステム(特にBSD系OS)でカーネルの実行時パラメータを検査および変更するためのメカニズムです。これにより、システム管理者はカーネルの動作を動的に調整したり、システム情報を取得したりすることができます。sysctl は通常、階層的な名前空間(例: kern.hostname, net.inet.ip.forwarding)を通じてパラメータにアクセスします。プログラムからは sysctl システムコールを介してこれらのパラメータにアクセスします。

sysctl システムコールは通常、以下の引数を取ります。

  • name: 問い合わせるカーネルパラメータのOID (Object Identifier) 配列。
  • namelen: name 配列の長さ。
  • oldp: 取得したデータを格納するバッファへのポインタ。
  • oldlenp: oldp が指すバッファのサイズへのポインタ。関数呼び出し後には、実際に書き込まれたデータのサイズが格納される。
  • newp: 設定する新しいデータへのポインタ(パラメータを変更する場合)。
  • newlen: newp が指すデータのサイズ(パラメータを変更する場合)。

データを取得する際、oldpNULL を渡し、oldlenp にバッファサイズを格納する変数のアドレスを渡すことで、必要なバッファサイズを事前に問い合わせることができます。これは、可変長のデータを扱う際によく用いられるパターンです。

OpenBSD

OpenBSDは、セキュリティを最優先事項として開発されているUnix系オペレーティングシステムです。その設計哲学は、コードの品質、堅牢性、そしてセキュリティの厳格な監査に重点を置いています。このため、他のOSと比較して、システムコールの扱いがより厳格であったり、特定のAPIの動作が異なる場合があります。今回の sysctl のバグも、OpenBSDの特定のバージョンにおける実装の詳細に起因するものです。

Go syscall パッケージ

Go言語の標準ライブラリには syscall パッケージが含まれています。このパッケージは、Goプログラムから直接オペレーティングシステムのシステムコールを呼び出すための低レベルなインターフェースを提供します。これにより、GoプログラムはOSの機能(ファイル操作、ネットワーク通信、プロセス管理、システム情報の取得など)に直接アクセスできます。syscall パッケージはOSごとに異なる実装を持ち、各OSのシステムコールAPIに合わせたラッパーを提供します。

kern.hostnamekern.domainname

これらはOpenBSDを含む多くのBSD系OSで利用される sysctl 変数です。

  • kern.hostname: システムのホスト名(コンピュータの名前)を保持します。
  • kern.domainname: システムのドメイン名(ネットワークドメインの名前)を保持します。

これらの情報は、ネットワーク上の識別やログ記録など、様々な場面で利用されます。

oldp パラメータとバッファサイズ問い合わせ

sysctl システムコールにおいて、oldp 引数に nil (またはC言語の NULL) を渡し、oldlenp に有効なポインタを渡すことで、カーネルは指定されたパラメータのデータを格納するために必要なバッファのサイズを oldlenp が指す変数に書き込みます。これは、呼び出し側が適切なサイズのバッファを動的に確保するために非常に重要な機能です。OpenBSD 5.0以前の特定のバージョンでは、kern.hostnamekern.domainname に対してこの方法で問い合わせた際に、必要なサイズが 0 と誤って報告されるバグがありました。

技術的詳細

Goの syscall パッケージ内の Sysctl 関数は、OpenBSD上で kern.hostnamekern.domainname のような特定の sysctl 変数を扱う際に、OpenBSD 5.0以前のバージョンに存在するバグを回避するためのワークアラウンドを含んでいました。

このバグは、sysctl システムコールが、kern.hostnamekern.domainname のような文字列型のカーネルパラメータに対して、必要なバッファサイズを問い合わせるために oldp 引数に nil を渡した場合に、常に 0 を返してしまうというものでした。本来であれば、文字列の実際の長さ(終端のNULL文字を含む)が返されるべきです。このため、Goの Sysctl 関数は、この 0 という誤った長さが返された場合に、固定の最大ホスト名長 (MAXHOSTNAMELEN、通常256バイト) を使用してバッファを確保するというロジックを持っていました。

しかし、このワークアラウンドの適用条件に問題がありました。元のコードでは、if OS == "openbsd" && (value == "kern.hostname" || value == "kern.domainname") という条件でワークアラウンドを適用しようとしていました。ここで valueSysctl 関数が最終的に返す文字列であり、これは sysctl システムコールから実際に取得されたデータ、またはエラーが発生した場合のダミー値です。

問題は、sysctl システムコールがバグによって 0 の長さを返した場合、Sysctl 関数はまだ正しいホスト名やドメイン名を取得できていないため、valuekern.hostnamekern.domainname と一致することはありえません。ワークアラウンドは、Sysctl 関数に渡された 入力パラメータ である name (つまり、どの sysctl 変数を問い合わせようとしているのか) に基づいて適用されるべきでした。

このコミットは、この論理的な誤りを修正し、ワークアラウンドの適用条件を name == "kern.hostname" || name == "kern.domainname" に変更することで、OpenBSDの特定のバージョンにおける sysctl のバグに正しく対処できるようにしました。これにより、GoプログラムがOpenBSD上でホスト名やドメイン名を安定して取得できるようになります。

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

src/pkg/syscall/syscall_bsd.go ファイルの以下の行が変更されました。

--- a/src/pkg/syscall/syscall_bsd.go
+++ b/src/pkg/syscall/syscall_bsd.go
@@ -563,7 +563,7 @@ func Sysctl(name string) (value string, err error) {
 		// Work around a bug that was fixed after OpenBSD 5.0.
 		// The length for kern.hostname and kern.domainname is always
 		// returned as 0 when a nil value is passed for oldp.
-		if OS == "openbsd" && (value == "kern.hostname" || value == "kern.domainname") {
+		if OS == "openbsd" && (name == "kern.hostname" || name == "kern.domainname") {
 			// MAXHOSTNAMELEN
 			n = 256
 		} else {

コアとなるコードの解説

変更された行は、Sysctl 関数内のOpenBSD固有のワークアラウンドの条件式です。

元のコード:

if OS == "openbsd" && (value == "kern.hostname" || value == "kern.domainname") {

この条件式では、オペレーティングシステムがOpenBSDであることに加えて、Sysctl 関数が返す value 変数が "kern.hostname" または "kern.domainname" と一致するかどうかをチェックしていました。しかし、前述の通り、valuesysctl システムコールから実際に取得されたデータであり、バグによって長さが 0 と返された場合、この value は期待するホスト名やドメイン名にはなりません。したがって、この条件は常に false となり、ワークアラウンドが適用されませんでした。

修正後のコード:

if OS == "openbsd" && (name == "kern.hostname" || name == "kern.domainname") {

修正後は、value の代わりに name 変数を使用しています。nameSysctl 関数に引数として渡される文字列であり、ユーザーがどの sysctl 変数(例: "kern.hostname")の情報を取得しようとしているのかを正確に示します。これにより、OpenBSDの特定のバージョンで kern.hostnamekern.domainname を問い合わせる場合にのみ、このワークアラウンドが正しく適用されるようになります。ワークアラウンドが適用されると、必要なバッファサイズ nMAXHOSTNAMELEN (256バイト) に設定され、ホスト名やドメイン名の取得が正常に行われるようになります。

この変更は、Goの syscall パッケージがOpenBSD上でより堅牢に動作するために不可欠な修正でした。

関連リンク

参考にした情報源リンク