JPCERT コーディネーションセンター

ENV34-C. getenv() が返す文字列へのポインタを保存しない

ENV34-C. getenv() が返す文字列へのポインタを保存しない

C 標準では getenv() の動作を次のように規定している。[ISO/IEC 9899:2011]

getenv 関数は、一致する並び(environment list)の要素に結び付けられた文字列へのポインタを返す。このポインタが指す文字列をプログラムで変更してはならないが、引き続く getenv 関数の呼出しで書き換わることがある。

ゆえに、getenv() が返すポインタは保存すべきでない。このポインタは、2 回目以降に getenv() 関数を呼び出したときに上書きされたり、putenv()setenv() の呼び出しによって環境変数の並びが変更された結果として無効になる可能性がある。getenv()が返すポインタを後で使用するために保存しておくと、ダングリングポインタとなったり、正しくないデータを指すようになるおそれがある。getenv() が返す文字列は、即座に参照して破棄するべきであり、後で使用する可能性がある場合は、文字列をコピーしておき、必要に応じて安全に参照できるようにする必要がある。

getenv() 関数はスレッドセーフではない。この関数を使用することで生じる可能性のある競合状態には必ず対処しておくこと。

違反コード

以下のコード例は、環境変数 TMP および TEMP の値を比較し、値が同じであるかを確認している。

char *tmpvar;
char *tempvar;

tmpvar = getenv("TMP");
if (!tmpvar) return -1;
tempvar = getenv("TEMP");
if (!tempvar) return -1;

if (strcmp(tmpvar, tempvar) == 0) {
  if (puts("TMP and TEMP are the same.\n") == EOF) {
    /* エラー処理 */
  }
}
else {
  if (puts("TMP and TEMP are NOT the same.\n") == EOF) {
    /* エラー処理 */
  }
}

getenv() 関数の 2 回目の呼び出しの結果、tmpvar によって参照される文字列が上書きされる可能性があるため、これは違反コードとなる。結果として、tmpvartempvar は、2つの環境変数の値が異なっていても等しいと見なされる可能性がある。

適合コード (Windows)

Windows では現在の環境から値を取得する関数として、getenv_s() および _wgetenv_s() が提供されている [MSDN]。

char *tmpvar;
char *tempvar;
size_t requiredSize;

getenv_s(&requiredSize, NULL, 0, "TMP");
tmpvar = (char *)malloc(requiredSize * sizeof(char));
if (!tmpvar) {
   /* エラー処理 */
}
getenv_s(&requiredSize, tmpvar, requiredSize, "TMP" );

getenv_s(&requiredSize, NULL, 0, "TEMP");
tempvar = (char *)malloc(requiredSize * sizeof(char));
if (!tempvar) {
   free(tmpvar);
   tmpvar = NULL;
   /* エラー処理 */
}
getenv_s(&requiredSize, tempvar, requiredSize, "TEMP" );

if (strcmp(tmpvar, tempvar) == 0) {
  if (puts("TMP and TEMP are the same.\n") == EOF) {
    /* エラー処理 */
  }
}
else {
  if (puts("TMP and TEMP are NOT the same.\n") == EOF) {
    /* エラー処理 */
  }
}
free(tmpvar);
tmpvar = NULL;
free(tempvar);
tempvar = NULL;
適合コード (Windows)

Windows では現在の環境から値を取得する関数として、_dupenv_s() および wdupenv_s() も提供されている [MSDN]。_dupenv_s() 関数は、環境変数の並びから指定された名前を検索する。この関数は、指定された名前を見つけるとバッファを割り当て、環境変数の値をバッファにコピーし、バッファのアドレスと要素の数を返す。_dupenv_s()_wdupenv_s() は、返り値をコピーするバッファの割り当て自体も行うため、getenv_s()_wgetenv_s() よりも便利な関数であるといえる。

これらの関数から返された割り当て済みのバッファは、呼び出し元のプログラムの責任で解放しなければいけない。

char *tmpvar;
char *tempvar;
size_t len;

errno_t err = _dupenv_s(&tmpvar, &len, "TMP");
if (err) return -1;
err = _dupenv_s(&tempvar, &len, "TEMP");
if (err) {
  free(tmpvar);
  tmpvar = NULL;
  return -1;
}

if (strcmp(tmpvar, tempvar) == 0) {
  if (puts("TMP and TEMP are the same.\n") == EOF) {
    /* エラー処理 */
  }
}
else {
  if (puts("TMP and TEMP are NOT the same.\n") == EOF) {
    /* エラー処理 */
  }
}
free(tmpvar);
tmpvar = NULL;
free(tempvar);
tempvar = NULL;
適合コード (POSIX)

POSIX では環境変数文字列のコピーを作成する関数として strdup() が提供されている [Open Group 2004]。strdup() 関数は「Extensions to the C Library—Part II」[ISO/IEC TR 24731-2:2010] にも組み込まれている。

char *tmpvar;
char *tempvar;

const char *temp = getenv("TMP");
if (temp != NULL) {
  tmpvar = strdup(temp);
  if (tmpvar == NULL) {
    /* エラー処理 */
  }
}
else {
  return -1;
}

temp = getenv("TEMP");
if (temp != NULL) {
  tempvar = strdup(temp);
  if (tempvar == NULL) {
    free(tmpvar);
    tmpvar = NULL;
    /* エラー処理 */
  }
}
else {
  free(tmpvar);
  tmpvar = NULL;
  return -1;
}

if (strcmp(tmpvar, tempvar) == 0) {
  if (puts("TMP and TEMP are the same.\n") == EOF) {
    /* エラー処理 */
  }
}
else {
  if (puts("TMP and TEMP are NOT the same.\n") == EOF) {
    /* エラー処理 */
  }
}
free(tmpvar);
tmpvar = NULL;
free(tempvar);
tempvar = NULL;
適合コード

以下のコードは、C の関数 malloc() および strcpy() のみを使用して、getenv() が返す文字列を動的に割り当てられたバッファにコピーする。

char *tmpvar;
char *tempvar;

const char *temp = getenv("TMP");
if (temp != NULL) {
  tmpvar = (char *)malloc(strlen(temp)+1);
  if (tmpvar != NULL) {
    strcpy(tmpvar, temp);
  }
  else {
    /* エラー処理 */
  }
}
else {
  return -1;
}

temp = getenv("TEMP");
if (temp != NULL) {
  tempvar = (char *)malloc(strlen(temp)+1);
  if (tempvar != NULL) {
    strcpy(tempvar, temp);
  }
  else {
    free(tmpvar);
    tmpvar = NULL;
    /* エラー処理 */
  }
}
else {
  free(tmpvar);
  tmpvar = NULL;
  return -1;
}

if (strcmp(tmpvar, tempvar) == 0) {
  if (puts("TMP and TEMP are the same.\n") == EOF) {
    /* エラー処理 */
  }
}
else {
  if (puts("TMP and TEMP are NOT the same.\n") == EOF) {
    /* エラー処理 */
  }
}
free(tmpvar);
tmpvar = NULL;
free(tempvar);
tempvar = NULL;
リスク評価

getenv() が返す文字列へのポインタを保存すると、環境変数の上書きにつながる可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

ENV34-C

P4

L3

自動検出(最新の情報はこちら

ツール

バージョン

チェッカー

説明

Compass/ROSE

 

 

 

関連するガイドライン
CERT C++ Secure Coding Standard ENV00-CPP. Do not store the pointer to the string returned by getenv()
ISO/IEC TR 24731-2  
ISO/IEC TS 17961 (ドラフト) Using an object overwritten by getenv, localeconv, setlocale, and strerror [libuse]
参考資料
[ISO/IEC 9899:2011] Section 7.22.4, "Communication with the Environment"
Section 7.22.4.6, "The getenv Function"
[MSDN] _dupenv_s() and _wdupenv_s()
getenv_s(), _wgetenv_s()
[Open Group 2004] Chapter 8, "Environment Variables"
strdup
[Viega 2003] Section 3.6, "Using Environment Variables Securely"
翻訳元

これは以下のページを翻訳したものです。

ENV34-C. Do not store the pointer to the string returned by getenv() (revision 61)

Top へ

Topへ
最新情報(RSSメーリングリストTwitter