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 ビットの限界には手が届きません。セッションは、プログラムがネットワーク接続を開くのを見つけると警告します。