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

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

このコミットでは、Go言語の標準ライブラリosおよびsyscallパッケージ内の環境変数設定に関する変更が行われています。具体的には、Unix系システムにおけるSetenv関数の挙動が修正され、環境変数名(キー)や値に無効な文字が含まれていないかどうかのチェックが追加されました。

変更されたファイルは以下の2つです。

  • src/pkg/os/env_unix_test.go: 新規追加されたテストファイルで、Setenv関数が無効な環境変数名や値に対して正しくエラー(EINVAL)を返すことを検証します。
  • src/pkg/syscall/env_unix.go: Setenv関数の実装が含まれるファイルで、環境変数名に=\x00(NULL文字)、環境変数値に\x00が含まれていないかをチェックするロジックが追加されました。

コミット

commit d2252d9b0769867fa8a25ba8b274603ddf21c9e9
Author: Péter Surányi <speter.go1@gmail.com>
Date:   Fri Feb 8 10:45:46 2013 -0800

    syscall: check for invalid characters in Setenv on Unix
    
    On POSIX, '=' in key is explicitly invalid, and '\x00' in key/value is implicitly invalid.
    
    R=golang-dev, iant, bradfitz
    CC=golang-dev
    https://golang.org/cl/7311061

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

https://github.com/golang/go/commit/d2252d9b0769867fa8a25ba8b274603ddf21c9e9

元コミット内容

syscall: check for invalid characters in Setenv on Unix

On POSIX, '=' in key is explicitly invalid, and '\x00' in key/value is implicitly invalid.

R=golang-dev, iant, bradfitz
CC=golang-dev
https://golang.org/cl/7311061

変更の背景

この変更の背景には、POSIX標準における環境変数の命名規則と、Go言語のSetenv関数がこれらの規則に準拠していなかったという問題があります。

POSIX(Portable Operating System Interface)は、UNIX系オペレーティングシステム間の互換性を高めるための標準規格です。環境変数に関しては、いくつかの重要な制約があります。

  1. 環境変数名(キー)に=を含めることの禁止: POSIXでは、環境変数はNAME=VALUEという形式で表現されます。このため、環境変数名自体に=が含まれていると、パーサーが名前と値の区切りを正しく識別できなくなり、予期せぬ動作を引き起こす可能性があります。
  2. 環境変数名および値にNULL文字(\x00)を含めることの禁止: C言語の文字列はNULL終端であるため、環境変数名や値にNULL文字が含まれていると、文字列が途中で切れてしまい、情報が正しく伝達されなくなります。これは、多くのシステムコールがNULL終端文字列を期待しているため、暗黙的に無効な文字とされています。

Go言語のSetenv関数は、これらのPOSIX標準の制約を明示的にチェックしていませんでした。その結果、無効な環境変数名や値が設定され、後続のプログラムやシステムコールで問題が発生する可能性がありました。このコミットは、このような潜在的な問題を未然に防ぐために、Setenv関数にこれらの無効文字チェックを追加し、POSIX標準への準拠を強化することを目的としています。

前提知識の解説

環境変数 (Environment Variables)

環境変数とは、オペレーティングシステムが提供する動的な名前付きの値の集合です。これらは、実行中のプロセスに設定情報や構成データを提供するために使用されます。例えば、PATH環境変数は実行可能ファイルの検索パスを定義し、HOME環境変数はユーザーのホームディレクトリを示します。

プログラムは、これらの環境変数を読み取って、その動作を調整することができます。Setenv関数(またはそれに相当するシステムコール)は、新しい環境変数を設定したり、既存の環境変数の値を変更したりするために使用されます。

Setenv関数

Setenvは、指定されたキー(環境変数名)と値で環境変数を設定する関数です。Go言語では、os.Setenvがこの機能を提供し、内部的にはsyscall.Setenvを呼び出します。

EINVAL (Invalid Argument)

EINVALは、Unix系システムコールが返すエラーコードの一つで、「無効な引数」を意味します。これは、関数に渡された引数が、その関数の期待する形式や範囲に合致しない場合に返されます。このコミットでは、無効な文字を含む環境変数名や値がSetenvに渡された際に、このEINVALエラーを返すように変更されています。

NULL文字 (\x00)

NULL文字(ヌル文字)は、ASCIIコードで0x00(10進数で0)に相当する特殊な文字です。C言語では、文字列の終端を示すマーカーとして広く使用されています。そのため、文字列の途中にNULL文字が含まれていると、それ以降のデータが無視される可能性があります。環境変数においても、この特性が問題となるため、キーや値にNULL文字を含めることはできません。

イコール記号 (=)

イコール記号は、環境変数のキーと値を区切るために使用されます(例: KEY=VALUE)。このため、環境変数名自体にイコール記号が含まれていると、システムがキーと値の境界を正しく認識できなくなり、環境変数の解析に問題が生じます。

技術的詳細

このコミットの技術的な変更は、主にsyscall/env_unix.go内のSetenv関数に、環境変数名と値のバリデーションロジックを追加することにあります。

変更前は、Setenv関数はキーが空文字列であるかどうかのチェックしか行っていませんでした。

if len(key) == 0 {
    return EINVAL
}

変更後、以下の2つのループが追加されました。

  1. キーのチェック:

    for i := 0; i < len(key); i++ {
        if key[i] == '=' || key[i] == 0 {
            return EINVAL
        }
    }
    

    このループは、環境変数名(key)の各文字を走査し、=または\x00(NULL文字)が含まれていないかをチェックします。これらの文字が検出された場合、即座にEINVALエラーを返します。

  2. 値のチェック:

    for i := 0; i < len(value); i++ {
        if value[i] == 0 {
            return EINVAL
        }
    }
    

    このループは、環境変数の値(value)の各文字を走査し、\x00(NULL文字)が含まれていないかをチェックします。NULL文字が検出された場合、即座にEINVALエラーを返します。

これらのチェックにより、Setenv関数はPOSIX標準に準拠し、無効な環境変数が設定されることを防ぐことができます。

また、src/pkg/os/env_unix_test.goに新しいテストケースTestSetenvUnixEinvalが追加されました。このテストは、以下の無効なキーと値の組み合わせに対してSetenvを呼び出し、期待通りEINVALエラーが返されることを確認します。

  • 空のキー ("", "")
  • キーに=が含まれる ("k=v", "")
  • キーに\x00が含まれる ("\x00", "")
  • 値に\x00が含まれる ("k", "\x00")

これらのテストは、追加されたバリデーションロジックが正しく機能することを保証します。

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

src/pkg/os/env_unix_test.go (新規追加)

--- /dev/null
+++ b/src/pkg/os/env_unix_test.go
@@ -0,0 +1,30 @@
+// Copyright 2013 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build darwin freebsd linux netbsd openbsd
+
+package os_test
+
+import (
+	. "os"
+	"testing"
+)
+
+var setenvEinvalTests = []struct {
+	k, v string
+}{
+	{"", ""},      // empty key
+	{"k=v", ""},   // '=' in key
+	{"\x00", ""},  // '\x00' in key
+	{"k", "\x00"}, // '\x00' in value
+}
+
+func TestSetenvUnixEinval(t *testing.T) {
+	for _, tt := range setenvEinvalTests {
+		err := Setenv(tt.k, tt.v)
+		if err == nil {
+			t.Errorf(`Setenv(%q, %q) == nil, want error`, tt.k, tt.v)
+		}
+	}
+}

src/pkg/syscall/env_unix.go

--- a/src/pkg/syscall/env_unix.go
+++ b/src/pkg/syscall/env_unix.go
@@ -71,6 +71,16 @@ func Setenv(key, value string) error {
 	if len(key) == 0 {
 		return EINVAL
 	}
+	for i := 0; i < len(key); i++ {
+		if key[i] == '=' || key[i] == 0 {
+			return EINVAL
+		}
+	}
+	for i := 0; i < len(value); i++ {
+		if value[i] == 0 {
+			return EINVAL
+		}
+	}
 
 	envLock.Lock()
 	defer envLock.Unlock()

コアとなるコードの解説

src/pkg/os/env_unix_test.go

このファイルは、osパッケージのテストの一部として、Unix系システム(Darwin, FreeBSD, Linux, NetBSD, OpenBSD)でのSetenvの挙動を検証するために新規に作成されました。

  • +build darwin freebsd linux netbsd openbsd: この行は、このファイルが指定されたOSでのみビルドされることを示します。
  • setenvEinvalTests変数: これは、無効なキーと値の組み合わせを定義した構造体のスライスです。各要素は、テスト対象のキー(k)と値(v)を含みます。
    • {"", ""}: 空のキーをテストします。
    • {"k=v", ""}: キーに=が含まれるケースをテストします。
    • {"\x00", ""}: キーにNULL文字が含まれるケースをテストします。
    • {"k", "\x00"}: 値にNULL文字が含まれるケースをテストします。
  • TestSetenvUnixEinval関数: このテスト関数は、setenvEinvalTestsの各要素をループで処理します。
    • err := Setenv(tt.k, tt.v): 定義された無効なキーと値でSetenvを呼び出します。
    • if err == nil: Setenvがエラーを返さなかった場合、それは予期せぬ動作であるため、t.Errorfを使ってテストを失敗させます。これにより、Setenvが無効な入力に対して正しくEINVALエラーを返すことを保証します。

src/pkg/syscall/env_unix.go

このファイルは、Go言語のsyscallパッケージの一部として、Unix系システムにおける環境変数操作の低レベルな実装を含んでいます。

  • func Setenv(key, value string) error: この関数は、指定されたkeyvalueで環境変数を設定します。
  • if len(key) == 0 { return EINVAL }: 既存のチェックで、キーが空文字列の場合はEINVALを返します。
  • 追加されたキーのバリデーション:
    for i := 0; i < len(key); i++ {
        if key[i] == '=' || key[i] == 0 {
            return EINVAL
        }
    }
    
    このループは、key文字列をバイト単位で走査します。key[i]は、文字列のi番目のバイト(文字)を表します。
    • key[i] == '=': 現在の文字がイコール記号であるかをチェックします。
    • key[i] == 0: 現在の文字がNULL文字(\x00)であるかをチェックします。 どちらかの条件が真であれば、EINVALエラーを即座に返して関数を終了します。
  • 追加された値のバリデーション:
    for i := 0; i < len(value); i++ {
        if value[i] == 0 {
            return EINVAL
        }
    }
    
    このループは、value文字列をバイト単位で走査します。
    • value[i] == 0: 現在の文字がNULL文字(\x00)であるかをチェックします。 NULL文字が検出された場合、EINVALエラーを即座に返して関数を終了します。

これらの追加されたチェックにより、Setenv関数は、無効な文字を含む環境変数名や値がシステムに渡されることを防ぎ、より堅牢な環境変数操作を提供します。

関連リンク

参考にした情報源リンク