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

MSC33-C. 無効なデータを asctime() 関数に渡さない

C 標準 [ISO/IEC 9899:2011] 7.27.3.1 節には、asctime() 関数の実装例が次のように示されている。

char *asctime(const struct tm *timeptr) {
  static const char wday_name[7][3] = {
    "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
  };
  static const char mon_name[12][3] = {
    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
  };
  static char result[26];
  sprintf(
    result,
    "%.3s %.3s%3d %.2d:%.2d:%.2d %d\n",
    wday_name[timeptr->tm_wday],
    mon_name[timeptr->tm_mon],
    timeptr->tm_mday, timeptr->tm_hour,
    timeptr->tm_min, timeptr->tm_sec,
    1900 + timeptr->tm_year
  );
  return result;
}

この関数は、終端ナル文字を含む最大26文字からなる文字列を出力する想定である。書式の指定が示す長さを数えると25文字になり、終端ナル文字を考慮しても、この文字列を収めるための配列サイズは十分であるようにみえる。

しかし、この処理系は struct tm の値が通常の範囲内にあることを想定しており、値の取りうる範囲を全く制限していない。値のいずれかが想定を超える文字数を出力した場合、sprintf() 関数は result 配列をオーバーフローさせる可能性がある。たとえば、tm_year の値が 12345 の場合、終端ナル文字を含み 27 文字出力され、バッファオーバーフローが発生する。

POSIX® Base Specifications は asctime() 関数と asctime_r() 関数について次のように規定している[IEEE Std 1003.1:2013]。

これらの関数は昔の処理系との互換性のためだけに提供される。結果の文字列が長すぎる場合の動作は未定義であるため、これらの関数の使用は推奨されない。出力文字長のオーバーフローを検出しない処理系においては、出力バッファがオーバーフローし、プログラムが終了したり、システムのセキュリティを侵害する可能性がある。また、これらの関数はローカライズされた日時フォーマットの使用をサポートしていない。これらの問題を回避する上でも、プログラムは、strftime() を使って時間を示す文字列を生成すべきである。

C 標準の附属書 Kでは、asctime() のセキュアな代替関数として asctime_s() を定義している。

asctime() 関数は「MSC24-C. 非推奨関数や時代遅れの関数を使用しない」において時代遅れの関数とされている。

違反コード

この違反コード例では、無害化されていない可能性のあるデータに対して asctime() 関数を呼び出している。

#include <time.h>

void func(struct tm *time_tm) {
  char *time = asctime(time_tm);
  /* ... */
}
適合コード (strftime())

strftime() 関数では、現在のロケールに応じた書式を指定することができる。また、結果の文字列の最大サイズも指定する。

#include <time.h>

enum { maxsize = 26 };

void func(struct tm *time) {
  char s[maxsize];
  /* 現在のロケールでの時刻表現 */
  const char *format = "%c";

  size_t size = strftime(s, maxsize, format, time);
}

この関数呼出しでは asctime() と同じ結果が得られるが、出力は maxsize 文字を超えないように制限され、バッファオーバーフローの発生を防止する。

適合コード (asctime_s())

C 標準の附属書 K では、asctime() 関数の代替関数として asctime_s() 関数を定義している。asctime_s() 関数では引数が1つ追加されており、出力する文字列の最大値を指定する。

#define __STDC_WANT_LIB_EXT1__ 1
#include <time.h>

enum { maxsize = 26 };

void func(struct tm *time_tm) {
  char buffer[maxsize];

  if (asctime_s(buffer, maxsize, &time_tm)) {
    /* エラー処理 */
  }
}
リスク評価

出力文字列長のオーバーフローを検出しない処理系では、出力バッファがオーバーフローする可能性がある。

ルール

深刻度

可能性

修正コスト

優先度

レベル

MSC33-C

P27

L1

関連するガイドライン
CERT C コーディングスタンダード MSC24-C. 非推奨関数や時代遅れの関数を使用しない
参考資料
[IEEE Std 1003.1:2013] XSH, System Interfaces, asctime
[ISO/IEC 9899:2011] 7.27.3.1, "The asctime Function"
翻訳元

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

MSC33-C. Do not pass invalid data to the asctime() function (revision 46)

Top へ

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