[インデックス 13280] ファイルの概要
このコミットは、Go言語のランタイム環境の一部であるlib9
ライブラリ内のp9getwd()
関数におけるメモリリークを修正するものです。具体的には、p9getwd()
が内部でp9getenv
を使用する際に発生していた、strdup
によるメモリ割り当ての解放漏れに対処しています。
コミット
commit d152321cea4ebee18b7b819d29d0718dbc139212
Author: Shenghou Ma <minux.ma@gmail.com>
Date: Tue Jun 5 01:31:23 2012 +0800
lib9: fix memory leak in p9getwd()
although the comment says it uses libc's getenv, without NOPLAN9DEFINES
it actually uses p9getenv which strdups.
R=golang-dev, dave, rsc
CC=golang-dev
https://golang.org/cl/6285046
GitHub上でのコミットページへのリンク
https://github.com/golang/go/commit/d152321cea4ebee18b7b819d29d0718dbc139212
元コミット内容
lib9
: p9getwd()
におけるメモリリークを修正。
コメントではlibc
のgetenv
を使用すると書かれているが、NOPLAN9DEFINES
がない場合、実際にはstrdup
を使用するp9getenv
が使われていた。
変更の背景
この変更は、Go言語の初期のランタイム環境において、現在の作業ディレクトリを取得するp9getwd()
関数に潜在していたメモリリークを解決するために行われました。
問題の根源は、コード内のコメントと実際の動作との乖離にありました。コメントではlibc
(標準Cライブラリ)のgetenv
関数を使用すると示唆されていましたが、特定のコンパイルフラグ(NOPLAN9DEFINES
)が定義されていない環境では、Goの内部実装であるp9getenv
が代わりに呼び出されていました。
p9getenv
は、環境変数の値を取得する際に、その文字列を複製するためにstrdup()
関数を使用していました。strdup()
は新しいメモリを動的に割り当てるため、その結果が適切に解放されないとメモリリークが発生します。このコミットは、この意図しないp9getenv
の使用を防ぎ、libc
のgetenv
が正しく使用されるようにすることで、メモリリークを解消することを目的としています。
前提知識の解説
lib9
: Go言語の初期のランタイム環境において、システムコールや基本的なユーティリティ関数を提供していたライブラリ群です。Go言語がPlan 9オペレーティングシステムの設計思想に強く影響を受けていたため、その名残としてlib9
という名称が使われていました。これは、GoプログラムがOSとやり取りするための低レベルなインターフェースを提供していました。p9getwd()
: Plan 9系のシステムコールやライブラリ関数に由来する命名規則を持つ関数で、現在の作業ディレクトリ(Current Working Directory, CWD)のパスを取得する役割を担っていました。Unix/Linuxにおけるgetcwd()
やWindowsにおける_getcwd()
に相当する機能です。libc
(標準Cライブラリ): C言語で書かれたプログラムがOSの機能を利用するための標準的な関数群を提供するライブラリです。ファイルI/O、メモリ管理、文字列操作、プロセス制御など、多岐にわたる機能が含まれます。getenv()
関数もこのlibc
の一部です。getenv()
:libc
に含まれる関数で、指定された環境変数の値を取得します。通常、この関数が返すポインタは、環境変数テーブル内の既存の文字列を指しており、呼び出し側がそのメモリを解放する必要はありません。p9getenv()
:lib9
またはGoの内部で定義されていた可能性のある、getenv()
に似た機能を提供する関数です。コミットメッセージによると、このp9getenv
は内部でstrdup()
を使用していたことが問題でした。strdup()
: C言語の標準ライブラリ関数の一つで、与えられた文字列を複製し、その複製された文字列を格納するための新しいメモリを動的に割り当てて、そのポインタを返します。この関数によって割り当てられたメモリは、使用後にfree()
関数で明示的に解放する必要があります。解放を怠るとメモリリークが発生します。NOPLAN9DEFINES
: これは、Go言語のビルドシステムやコンパイルプロセスにおいて使用されるプリプロセッサマクロ(#define
)です。このマクロが定義されている場合、GoのソースコードはPlan 9固有の定義や関数ではなく、より一般的なUnix/POSIX互換の定義や関数(例えば、libc
の関数)を使用するようにコンパイルされます。このコミットの文脈では、NOPLAN9DEFINES
が定義されていないと、getenv
の呼び出しが意図せずp9getenv
にリダイレクトされてしまう問題がありました。
技術的詳細
このメモリリークは、Goのsrc/lib9/getwd.c
ファイル内のp9getwd()
関数が、環境変数を取得する際に誤った関数パスを使用していたことに起因します。
- 意図された動作: コードのコメントや設計意図としては、
p9getwd()
はlibc
の標準的なgetenv()
関数を使用して環境変数を取得するはずでした。libc
のgetenv()
は、通常、内部の静的バッファや環境変数テーブルへのポインタを返すため、呼び出し側がメモリを解放する必要はありません。 - 実際の動作: しかし、
NOPLAN9DEFINES
というプリプロセッサマクロが定義されていないビルド環境では、getenv
というシンボルがGoの内部実装であるp9getenv
に解決されてしまっていました。これは、GoがPlan 9の環境をエミュレートする際に、標準Cライブラリの関数をGo独自のラッパー関数でオーバーライドするメカニズムがあったためと考えられます。 - メモリリークの原因: 問題は、この
p9getenv
が、取得した環境変数の文字列を返す際に、strdup()
関数を使用してその文字列のコピーを作成していた点にあります。strdup()
はヒープメモリを動的に割り当てるため、この割り当てられたメモリは、p9getwd()
の呼び出し元やp9getenv
の内部で明示的にfree()
される必要がありました。しかし、この解放処理が欠けていたため、p9getwd()
が呼び出されるたびに、strdup()
によって割り当てられたメモリが解放されずに残り、メモリリークが発生していました。 - 修正方法: 修正は非常にシンプルかつ効果的です。
src/lib9/getwd.c
のインクルードセクションに#define NOPLAN9DEFINES
を追加することで、このファイルがコンパイルされる際に、getenv
などのシンボルがlibc
の標準的な定義に解決されるように強制します。これにより、p9getenv
が呼び出されることを防ぎ、代わりにlibc
のgetenv
が使用されるようになります。libc
のgetenv
はstrdup
を使用しないため、メモリリークの問題が解消されます。 #undef getwd
の削除: 以前のコードには#undef getwd
という行がありましたが、これはgetwd
マクロの定義を解除するためのものでした。NOPLAN9DEFINES
が追加されたことで、getwd
がp9getwd
にマッピングされるようなPlan 9固有の定義がそもそも適用されなくなるため、この#undef
は不要となり削除されました。
この修正は、Goのランタイムが異なるOS環境(特にUnix系)で正しく動作し、メモリ効率を保つ上で重要なものでした。
コアとなるコードの変更箇所
--- a/src/lib9/getwd.c
+++ b/src/lib9/getwd.c
@@ -26,10 +26,9 @@ THE SOFTWARE.
#include <u.h>
#include <errno.h>
#include <sys/stat.h>
+#define NOPLAN9DEFINES
#include <libc.h>\n
-#undef getwd
-\n
char*\n
p9getwd(char *s, int ns)\n
{
コアとなるコードの解説
変更はsrc/lib9/getwd.c
ファイル内の2行の削除と1行の追加です。
-
#define NOPLAN9DEFINES
の追加:- この行が追加されたことで、
getwd.c
がコンパイルされる際に、GoのビルドシステムはPlan 9固有の定義やマクロを適用せず、標準Cライブラリ(libc.h
)の定義を優先するようになります。 - これにより、
getenv
のような関数呼び出しが、Goの内部実装であるp9getenv
ではなく、システム標準のgetenv
に正しく解決されるようになります。システム標準のgetenv
は、通常、返された文字列のメモリを呼び出し側が解放する必要がないため、strdup
によるメモリリークの問題が解消されます。
- この行が追加されたことで、
-
#undef getwd
の削除:- 以前のコードには
#undef getwd
という行がありましたが、これはgetwd
というマクロの定義を解除するためのものでした。 NOPLAN9DEFINES
が追加されたことで、getwd
がPlan 9固有の定義によってマクロとして定義されることがなくなるため、この#undef
は冗長となり削除されました。これにより、コードがよりクリーンになり、意図しないマクロの干渉を防ぐことができます。
- 以前のコードには
これらの変更により、p9getwd()
関数が環境変数を扱う際のメモリ管理が正しく行われるようになり、メモリリークが修正されました。
関連リンク
- Go CL 6285046: https://golang.org/cl/6285046
参考にした情報源リンク
strdup
man page: https://man7.org/linux/man-pages/man3/strdup.3.htmlgetenv
man page: https://man7.org/linux/man-pages/man3/getenv.3.html- Go言語の初期の歴史とPlan 9の影響に関する一般的な情報源 (例: Go言語の公式ドキュメント、関連する技術ブログなど)
- C言語のプリプロセッサディレクティブに関する一般的な情報源 (例: C言語の教科書、オンラインリファレンスなど)
- Go言語のソースコードリポジトリ (特に
src/lib9
ディレクトリの歴史)