Chrono Mock
言語日本語

Windows での 2038 年問題をテストする

2038 年 1 月 19 日 03:14:07 UTC に、1970 年からの秒数を数える符号付き 32 ビットのカウンターが尽きます。Windows の時計はその先も進みますが、独自の 32 ビットのコピーを持ち続けたプログラム、ファイル形式、ライブラリはそうではありません。マシン全体の日付を 2038 年に設定せずに、それらを 1 つのアプリで見つける方法を紹介します。

バグがまだ潜みうる場所

Windows の時計は 1601 年から 100 ナノ秒刻みで数える 64 ビットのカウンターなので、2038 年はありません。C ランタイムライブラリの time_t は、Visual Studio 2005 以降、既定で 64 ビットです。残るのは、32 ビットの時刻を選んだコード、あるいは途中で上位の半分を失ったコードです。

  • _USE_32BIT_TIME_T を付けてコンパイルした 32 ビットビルド、または _time32 や __time32_t を直接呼ぶもの。Microsoft 自身のドキュメントも、そのようなアプリは 2038 年 1 月 18 日以降に失敗する可能性があると警告しています。
  • 64 ビットの時刻を int や long に格納しているもの。 Windows ではどちらも 32 ビットで、64 ビットのプログラムでも同じです。
  • 1970 年からの秒数を 32 ビットで保持するライブラリ、ファイル形式、ネットワークプロトコル、データベース列。
  • 年を加算するコード。 20 年のライセンス、30 年の返済スケジュール、2040 年まで有効な証明書は、2038 年が来るずっと前にこの境界をまたぎます。

1 つのアプリで境界をまたぐ

この限界は UTC で定義されていて、--at はセッションのゾーンで読まれます。時刻を自分で書くときは、ゾーンを UTC に固定してください。用意されたシナリオには絶対的な UTC の時刻がすでに含まれているので、ゾーンは不要です。

# シナリオ: セッションは限界そのもの、03:14:07 UTC から始まる
chrono run "C:\apps\test.exe" --preset year-2038 --report session.txt

# 17 秒前から始めて、アプリに境界をその場でまたがせる
chrono run "C:\apps\test.exe" --at 2038-01-19T03:13:50 --zone +00:00 --report session.txt

# もっと早くから 60 倍速で実行 - 約 14 秒で限界に到達する
chrono run "C:\apps\test.exe" --at 2038-01-19T03:00:00 --zone +00:00 --mode x60

# あるいは今日の日付で実行し、アプリを開いたまま境界を飛び越える
chrono run "C:\apps\test.exe" --jump-after 5:2038-01-19T03:14:05 --zone +00:00

限界の 1 秒後、符号付き 32 ビットのカウンターは -2,147,483,648 になり、これは 1901 年 12 月 13 日 20:45:52 UTC と読まれます。1901 年の日付、負の数、突然期限切れになる項目や並び順の先頭に来る項目、日付処理のコードから投げられる例外。オーバーフローは外から見るとこうした形で現れます。

本物の時計を 2038 年に設定するのは、ここへ至るもう 1 つの方法で、最も多くのものを壊す方法です。システム日付を変更すると何が起きるか →

プログラムに届いたことを確認する

プログラムが 2038 年を一度も見なかったために通ったテストは、テストがないよりも悪いものです。すべてのセッションの最後のレポートは、プログラムがどの時計を何回読んだかを示します。

Chrono Mock - session report
  target:   .\build\test.exe
  verdict:  WORKS - time substitution took effect  (processes: 1)
            every process that read time saw the session clock
  session:  fake clock reached 2038-01-19T03:14:08
            real elapsed 1.3s, fake elapsed 1.3s
  covered channels (substituted, with call counts, 10 calls in all):
            - pid 21240: GetSystemTimeAsFileTime (5 calls)
            - pid 21240: GetLocalTime (5 calls)

session の行は、アプリの時計が限界を越えたことを裏付けます。呼び出し回数が付いた works の判定は、プログラムが時計を読み、2038 年の日付を受け取ったことを意味します。works partially は、別の経路で読まれたチャネルを挙げます。判定の意味 →

テストが通っても証明されないこと

  • 動いたのはプログラム自身の時計だけです。 ファイルやデータベースから読み込む日付は、そこに保存されているとおりのものなので、データの中にある 2038 年の日付は別のテストです。日付電卓なら、そうした日付を代わりに計算できます。chrono calc --base 2038-01-19T03:14:07 --zone +00:00 --shift +1s
  • プログラムが通信するサーバーは、本物の日付で動き続けています。 その側の 32 ビットの限界には手が届きません。セッションは、プログラムがネットワーク接続を開くのを見つけると警告します。

試してみる