Go、後方互換性、およびGODEBUG
はじめに
Goの後方互換性への重点は、その主要な強みの1つです。しかし、完全な互換性を維持できない場合もあります。コードがバグのある(セキュリティ上の問題を含む)動作に依存している場合、バグを修正するとそのコードが壊れます。新しい機能も同様の影響を与える可能性があります。HTTPクライアントによるHTTP/2の使用を有効にすると、バグのあるHTTP/2実装を持つサーバーに接続するプログラムが壊れました。このような変更は避けられず、Go 1互換性ルールで許可されています。それにもかかわらず、GoはGODEBUGと呼ばれるメカニズムを提供して、新しいツールチェーンを使用して古いコードをコンパイルするGo開発者への影響を軽減します。
GODEBUG設定は、Goプログラムの特定の部分の実行を制御するkey=value
ペアです。環境変数GODEBUG
には、これらの設定をコンマで区切ったリストを含めることができます。たとえば、Goプログラムが次の環境で実行されている場合
GODEBUG=http2client=0,http2server=0
そのGoプログラムは、HTTPクライアントとHTTPサーバーの両方でHTTP/2の使用をデフォルトで無効にします。特定のプログラムのデフォルトのGODEBUG
を設定することも可能です(後述)。
Go 1互換性によって許可されているが、既存のプログラムの一部を壊す可能性のある変更を準備する際には、まず、できるだけ多くの既存のプログラムが動作し続けるように変更を設計します。残りのプログラムについては、個々のプログラムが古い動作をオプトインできるようにする新しいGODEBUG設定を定義します。そうすることが不可能な場合は、GODEBUG設定を追加しない場合がありますが、これは非常にまれであるはずです。
互換性のために追加されたGODEBUG設定は、最低2年間(4つのGoリリース)維持されます。http2client
やhttp2server
などの一部の設定は、はるかに長く、場合によっては無期限に維持されます。
可能な限り、各GODEBUG設定には、/godebug/non-default-behavior/<name>:events
という名前の関連するruntime/metricsカウンターがあり、特定のプログラムの動作がその設定の非デフォルト値に基づいて変更された回数をカウントします。たとえば、GODEBUG=http2client=0
が設定されている場合、/godebug/non-default-behavior/http2client:events
は、プログラムがHTTP/2サポートなしで構成したHTTPトランスポートの数をカウントします。
デフォルトのGODEBUG値
GODEBUG設定が環境変数にリストされていない場合、その値は3つのソースから取得されます。プログラムの構築に使用されたGoツールチェーンのデフォルト値で、go.mod
にリストされているGoバージョンに一致するように修正され、その後、プログラム内の明示的な//go:debug
行によってオーバーライドされます。
GODEBUG履歴は、各Goツールチェーンバージョンの正確なデフォルト値を示しています。たとえば、Go 1.21はpanicnil
設定を導入し、panic(nil)
が許可されるかどうかを制御します。これはpanicnil=0
をデフォルトとしており、panic(nil)
をランタイムエラーにします。panicnil=1
を使用すると、Go 1.20以前の動作が復元されます。
古いGoバージョンを宣言するワークモジュールまたはワークスペースをコンパイルする場合、Goツールチェーンはそのデフォルト値をその古いGoバージョンにできるだけ近づけて修正します。たとえば、Go 1.21ツールチェーンがプログラムをコンパイルする場合、ワークモジュールのgo.mod
またはワークスペースのgo.work
にgo
1.20
と記載されている場合、プログラムはGo 1.21ではなくGo 1.20と一致するpanicnil=1
をデフォルトとして使用します。
このGODEBUGデフォルトの設定方法はGo 1.21で導入されただけなので、Go 1.20より前のバージョンをリストしているプログラムは、古いバージョンではなくGo 1.20と一致するように構成されます。
Go 1.23以降、これらのデフォルトをオーバーライドするには、ワークモジュールのgo.mod
またはワークスペースのgo.work
に1つ以上のgodebug
行をリストできます。
godebug (
default=go1.21
panicnil=1
asynctimerchan=0
)
特別なキーdefault
は、未指定の設定を取得するGoバージョンを示します。これにより、モジュールのGo言語バージョンとは別にGODEBUGのデフォルトを設定できます。この例では、プログラムはGo 1.21のセマンティクスを要求し、その後、古いGo 1.21以前のpanic(nil)
動作と新しいGo 1.23のasynctimerchan=0
動作を要求しています。
godebug
ディレクティブには、ワークモジュールのgo.mod
のみが参照されます。必要な依存関係モジュール内のディレクティブは無視されます。認識されない設定でgodebug
をリストすることはエラーです。(Go 1.23より前のツールチェーンは、godebug
をまったく理解していないため、すべてのgodebug
行を拒否します。)
go
とgodebug
行からのデフォルトは、ビルドされるすべてのメインパッケージに適用されます。より細かい制御を行うために、Go 1.21以降、メインパッケージのソースファイルは、ファイルの先頭(package
ステートメントの前に)に1つ以上の//go:debug
ディレクティブを含めることができます。前の例にあるgodebug
行は次のように記述されます。
//go:debug default=go1.21
//go:debug panicnil=1
//go:debug asynctimerchan=0
Go 1.21以降、Goツールチェーンは、認識されないGODEBUG設定を含む//go:debug
ディレクティブを無効なプログラムとして扱います。特定の設定に対して複数の//go:debug
行を持つプログラムも無効として扱われます。(古いツールチェーンは//go:debug
ディレクティブを完全に無視します。)
メインパッケージにコンパイルされるデフォルトは、次のコマンドによって報告されます。
go list -f '{{.DefaultGODEBUG}}' my/main/package
基本的なGoツールチェーンのデフォルトからの違いのみが報告されます。
パッケージをテストする場合、*_test.go
ファイル内の//go:debug
行は、テストのメインパッケージのディレクティブとして扱われます。その他のコンテキストでは、//go:debug
行はツールチェーンによって無視されます。go
vet
は、そのような行を誤った場所にあるものとして報告します。
GODEBUG履歴
このセクションでは、互換性の理由から各主要なGoリリースで導入および削除されたGODEBUG設定について説明します。パッケージまたはプログラムは、内部デバッグの目的で追加の設定を定義する場合があります。たとえば、runtimeドキュメントとgoコマンドドキュメントを参照してください。
Go 1.23
Go 1.23では、package timeによって作成されるチャネルが非バッファード(同期)に変更されました。これにより、Timer.Stop
メソッドとTimer.Reset
メソッドの結果の正しい使用方法がはるかに容易になります。asynctimerchan
設定はこの変更を無効にします。この変更に関するランタイムメトリクスはありません。この設定は、将来のリリース(早ければGo 1.27)で削除される可能性があります。
Go 1.23では、リパー スポイントについてos.Lstat
とos.Stat
によって報告されるモードビットが変更されました。これはwinsymlink
設定で制御できます。Go 1.23(winsymlink=1
)以降、マウントポイントにはos.ModeSymlink
が設定されなくなり、シンボリックリンク、Unixソケット、重複ファイルではないリパー スポイントには常にos.ModeIrregular
が設定されるようになりました。これらの変更の結果、filepath.EvalSymlinks
はマウントポイントを評価しなくなりました。これは多くの矛盾とバグの原因となっていました。以前のバージョン(winsymlink=0
)では、マウントポイントはシンボリックリンクとして扱われ、デフォルト以外のos.ModeType
ビット(os.ModeDir
など)を持つ他のリパー スポイントにはModeIrregular
ビットが設定されません。
Go 1.23では、os.Readlink
とfilepath.EvalSymlinks
が、ボリュームをドライブ文字に正規化しようとしないように変更されました。これは常に可能だったわけではありません。この動作はwinreadlinkvolume
設定で制御されます。Go 1.23では、デフォルトはwinreadlinkvolume=1
です。以前のバージョンでは、デフォルトはwinreadlinkvolume=0
でした。
Go 1.23では、実験的なポスト量子鍵交換メカニズムX25519Kyber768Draft00がデフォルトで有効になりました。デフォルトはtlskyber
設定を使用して元に戻すことができます。
Go 1.23では、負のシリアル番号を拒否するようにcrypto/x509.ParseCertificateの動作が変更されました。この変更はx509negativeserial
設定で元に戻すことができます。
Go 1.23では、デフォルトでhtml/templateのECMAScript 6テンプレートリテラルのサポートが再び有効になりました。jstmpllitinterp
設定はもはや効果がありません。
Go 1.23では、明示的に構成されていない場合にクライアントとサーバーで使用されるデフォルトのTLS暗号スイートが変更され、3DES暗号スイートが削除されました。デフォルトはtls3des
設定を使用して元に戻すことができます。
Go 1.23では、返されるtls.Certificate
のLeafフィールドを設定するようにtls.X509KeyPair
とtls.LoadX509KeyPair
の動作が変更されました。この動作はx509keypairleaf
設定で制御されます。Go 1.23では、デフォルトはx509keypairleaf=1
です。以前のバージョンでは、デフォルトはx509keypairleaf=0
でした。
Go 1.23では、エラーの処理時にCache-Control、Content-Encoding、Etag、Last-Modifiedヘッダーを削除するようにnet/http.ServeContent
、net/http.ServeFile
、net/http.ServeFS
が変更されました。この動作はhttpservecontentkeepheaders
設定で制御されます。httpservecontentkeepheaders=1
を使用すると、Go 1.23以前の動作が復元されます。
Go 1.22
Go 1.22では、TLSハンドシェイクで使用できる最大許容RSAキーサイズを制御する構成可能な制限が追加され、tlsmaxrsasize
設定で制御されます。デフォルトはtlsmaxrsasize=8192で、RSAを8192ビットキーに制限します。サービス拒否攻撃を防ぐために、この設定とデフォルトはGo 1.19.13、Go 1.20.8、Go 1.21.1にバックポートされました。
Go 1.22では、net/httpクライアントまたはサーバーによって読み取られるリクエストまたはレスポンスに空のContent-Lengthヘッダーが含まれている場合、エラーとなるようになりました。この動作はhttplaxcontentlength
設定によって制御されます。
Go 1.22では、ServeMuxの動作が変更され、拡張パターンを受け入れ、セグメントごとにパターンとリクエストパスの両方をエスケープ解除するようになりました。この動作はhttpmuxgo121
設定で制御できます。
Go 1.22では、型エイリアスの明示的な表現のために、go/typesにAlias型が追加されました。型チェッカーがAlias
型を生成するかどうかは、gotypesalias
設定によって制御されます。Go 1.22では、デフォルトはgotypesalias=0
です。Go 1.23では、gotypesalias=1
がデフォルトになります。この設定は将来のリリース(Go 1.27以降)で削除される予定です。
Go 1.22では、サーバーとクライアントの両方でサポートされるデフォルトの最小TLSバージョンがTLS 1.2に変更されました。デフォルトはtls10server
設定を使用してTLS 1.0に戻すことができます。
Go 1.22では、明示的に設定されていない場合のクライアントとサーバーで使用されるデフォルトのTLS暗号スイートが変更され、RSAベースの鍵交換を使用する暗号スイートが削除されました。デフォルトはtlsrsakex
設定を使用して戻すことができます。
Go 1.22では、接続がTLS 1.3と拡張マスターシークレット(Go 1.21で実装)のどちらもサポートしていない場合、ConnectionState.ExportKeyingMaterial
が無効化されました。これはtlsunsafeekm
設定で有効化できます。
Go 1.22では、ランタイムがLinuxの透過巨大ページとどのようにやり取りするかが変更されました。特に、一般的なデフォルトのLinuxカーネル設定では、メモリオーバーヘッドが大きくなる可能性があり、Go 1.22ではこのデフォルトを回避しなくなりました。カーネル設定を調整せずにこの問題を回避するには、disablethp
設定を使用してGoメモリの透過巨大ページを無効化できます。この動作はGo 1.21.1にバックポートされましたが、この設定はGo 1.21.6以降でのみ使用可能です。この設定は将来のリリースで削除される可能性があり、この問題の影響を受けるユーザーは、GCガイドの推奨事項に従ってLinuxの設定を調整するか、透過巨大ページを完全に無効化するLinuxディストリビューションに切り替える必要があります。
Go 1.22では、mutex
プロファイルにランタイム内部ロックの競合が追加されました。これらのロックの競合は常にruntime._LostContendedRuntimeLock
で報告されます。ランタイムロックの完全なスタックトレースは、runtimecontentionstacks
設定で有効にすることができます。これらのスタックトレースは非標準のセマンティクスを持っています。詳細については、設定のドキュメントを参照してください。
Go 1.22では、新しいcrypto/x509.Certificate
フィールドPolicies
が追加され、31ビットを超えるコンポーネントを持つ証明書ポリシーOIDをサポートするようになりました。デフォルトでは、このフィールドは解析中にのみ使用され、ポリシーOIDで設定されますが、マーシャリング中には使用されません。既存のPolicyIdentifiersフィールドの代わりに、これらのより大きなOIDをマーシャリングするために、x509usepolicies
設定を使用できます。
Go 1.21
Go 1.21では、nilインターフェース値でpanic
を呼び出すと、ランタイムエラーになるようになりました。これはpanicnil
設定によって制御されます。
Go 1.21では、html/templateアクションがECMAScript 6テンプレートリテラル内に現れるとエラーになるようになりました。これはjstmpllitinterp
設定によって制御されます。この動作はGo 1.19.8+およびGo 1.20.3+にバックポートされました。
Go 1.21では、MIMEヘッダーとマルチパートフォームの最大数の制限が導入されました。これはそれぞれmultipartmaxheaders
とmultipartmaxparts
設定によって制御されます。この動作はGo 1.19.8+およびGo 1.20.3+にバックポートされました。
Go 1.21ではMultipath TCPのサポートが追加されましたが、アプリケーションが明示的に要求した場合にのみ使用されます。この動作はmultipathtcp
設定で制御できます。
これらの設定を削除する予定はありません。
Go 1.20
Go 1.20では、tarおよびzipアーカイブ内の安全でないパスの拒否がサポートされました。これはtarinsecurepath
設定とzipinsecurepath
設定によって制御されます。これらはtarinsecurepath=1
とzipinsecurepath=1
がデフォルトで、以前のバージョンのGoの動作を維持しています。将来のGoバージョンでは、デフォルトがtarinsecurepath=0
とzipinsecurepath=0
に変更される可能性があります。
Go 1.20では、math/rand
グローバル乱数生成器の自動シード生成が導入されました。これはrandautoseed
設定によって制御されます。
Go 1.20では、証明書検証中に使用するフォールバックルートの概念が導入されました。これはx509usefallbackroots
設定によって制御されます。
Go 1.20では、Goディストリビューションから標準ライブラリのプリインストールされた.a
ファイルが削除されました。インストールでは、標準ライブラリは他のモジュールのパッケージのようにビルドおよびキャッシュされるようになりました。installgoroot
設定は、プリインストールされた.a
ファイルのインストールと使用を復元します。
これらの設定を削除する予定はありません。
Go 1.19
Go 1.19では、パスのルックアップが現在のディレクトリのバイナリに解決されるとエラーになるようになりました。これはexecerrdot
設定によって制御されます。この設定を削除する予定はありません。
Go 1.19では、DNSリクエストでEDNS0追加ヘッダーを送信するようになりました。これは、CenturyLink Zyxel C3000Zなどの一部のルータで提供されるDNSサーバーを壊す可能性があると報告されています。これはnetedns0
設定で変更できます。この設定はGo 1.21.12、Go 1.22.5、Go 1.23以降で使用可能です。この設定を削除する予定はありません。
Go 1.18
Go 1.18では、ほとんどのX.509証明書でSHA1のサポートが削除されました。これはx509sha1
設定によって制御されます。この設定は将来のリリース(Go 1.22以降)で削除される予定です。
Go 1.10
Go 1.10では、ビルドキャッシングの動作が変更され、テストキャッシングが追加されました。gocacheverify
、gocachehash
、gocachetest
設定も追加されました。これらの設定を削除する予定はありません。
Go 1.6
Go 1.6では、HTTP/2の透過的なサポートが導入されました。これはhttp2client
、http2server
、http2debug
設定によって制御されます。これらの設定を削除する予定はありません。
Go 1.5
Go 1.5では、純粋なGo DNSリゾルバーが導入されました。これはnetdns
設定によって制御されます。この設定を削除する予定はありません。