OS

Linux/Windowsにおける2038年問題の対策【まとめ】

製品に搭載する組込みソフトウェアなどを開発していると、
そろそろ「2038年問題」を意識し始めている方も多いのではないでしょうか?

本記事では、2038年問題とは何か、どういった対策方法があるのかを中心に解説します。

 

2038年問題とは?

2038年1月19日 3時14分7秒(UTC)を過ぎると、コンピュータが誤動作する可能性がある問題です。

 

誤動作するメカニズム

コンピュータの内部で動作するソフトウェアは、○年△月□日 ●時▲分■秒のような
実時間(RTC:Real-Time clock)」を扱います。
ファイルの作成日時や更新日時、時計表示など目に触れることも多いかと思います。

この実時間を1つの整数で表現しているコンピュータ、およびコンピュータプログラムがあり、
「1970年1月1日 0時0分0秒(UTC)」からの経過秒数を採用しているシステムがあります。

SANACHAN
SANACHAN
UNIXやLinuxはもちろん、そこから派生した OS で広く採用されています。

 

前述の「経過時間」は、ソフトウェア内部では「time_t型」で表現されています。
伝統的な実装で time_tint(符号付き32bit)または long(64bit OSでは 64bit、32bit なら 32bit)です。
つまり、32bit だと正数の最大値は 2,147,483,647 となり、約68年のカウントまでしかできません。

そのため、1970年に加算して2038年に「経過秒数」がオーバーフローし、
負の値となることで誤動作する可能性があるということです。

 

問題となる条件

以下の「AND」で2038年問題が発生する可能性の条件が成立します。

  • 実時間(RTC)を「1970年1月1日 0時0分0秒(UTC)」からの経過秒数で管理
  • 経過秒数を符号付き32bitで保持、またはアクセスする

 

どのような誤動作が考えられる?

実時間(時刻)を使ってどのような処理を行っているかによるため、
一概にコレだ!とは言えませんが、プログラム視点では以下のような問題が生じます。

  • 異常値として扱われる
    負の値となることで、正常値と見なされなくなる可能性があります。
  • 論理が反転する(=時系列が崩れる、など)
    大小比較などを行っている場合、論理が反転することになります。
  • 誤った演算結果となる(=経過時間、うるう年、など)
    演算を行っている箇所では、正負反転で誤った結果を得ることになります。
SANACHAN
SANACHAN
少し考えただけで色々出てきますね。
実際はこれらの複合問題もあり、表面上に出てくることになるでしょう。

 

よくある誤解

「2038年問題を対策したOSを搭載すれば問題ない」と考えている方も多いのではないでしょうか?
例えば、「64bit Linux を搭載してればOK」みたいな感じです。

これは、大きな間違いです!

この後、順に解説していきます。

 

実時間(RTC)の所在と利用者

まず、RTCを情報として保持している場所、及び利用していそうなソフトウェアの図を見てみましょう。
※Windowsはブラックボックスのため想定で描いていますが、機能としては存在しているはずです。

Hardware層

  • RTC Module
    実時間をカウントする専用のモジュールです。
    Chipset群に含まれる場合もありますが、当然「RTC」の情報を保持しています。
  • CMOS
    BIOS や UEFI が搭載されている場合にセットで搭載されます。
    BIOS や UEFI で設定した実時間を、ここに「RTC」の情報として保存されます。
  • FlashROM
    BIOS/UEFI や、Intel TXE(Trusted Execusion Engine)のファームウェアが格納されています。
    CPUで動作させるプログラムの格納先とは別のデバイスです。
    最近のファームは、ファイルシステムを使用しているため、ココにも「RTC」の情報があります。
  • HDD/SSD
    BOOTプログラム、OS、アプリケーション、など、大半のソフトウェアやデータが格納されています。
    こちらもファイルシステムを利用しているため、「RTC」の情報があります。

メモ

ファイルシステムでは、ファイルの「作成日」「更新日」に利用している他、
パーティションを管理する情報(Super Block)にも利用しています。

 

Software層

  • OS
    デバイスから取得したRTC情報や、上位から設定されたRTC情報をメモリ上に保存しています。
    これを参照して、プロトコルスタックやファイルシステムを扱うドライバを経由し、
    外部機器と通信したり、データをSSD/HDDに書き出したりしています。
  • LibraryまたはRuntime/Framework
    システムコールやサービスコールをラップし、上位アプリが利用しやすいAPIを提供しています。
    Linuxでは glibc, pthread, curl などが RTC 情報へアクセスします。
    gettimeofday, ftime, ctime, pthread_cond_timedwait, strftime などです。
    Windows では、アプリケーションを実行する際に必要となる Runtime/Framework が該当します。
  • Daemon/Service
    TCP/IPの上位アプリケーション層として NTP, RTP, RTCP などが実時間を扱うプロトコルです。
    それらのサービスやデーモンが RTC 情報へアクセスします。
  • Application
    メーカーが独自に実装する中心となる部分。この図では、ミドルウェアもこの層に該当します。
    機器の仕様や実現していることに大きく依存しますが、ログや経過時間の計算などで
    少なくとも RTC 情報を参照しているでしょう。
SANACHAN
SANACHAN
OSが2038年問題を解消しているからと言って、
「問題ない」と言えない感じがしてきましたね。

 

実時間(RTC)にアクセスする経路

アクセスする経路を見極めることが、対策を考える際に重要となります。

上図の通り、RTCの情報はハードウェアまたはハードウェアに近い層に保存されています。
そのため、NTPで取得した時刻の設定時や、ファイル操作行う際、間の層を必ず通過することになります。

また、ライブラリやランタイム自身が独自にアクセスする場合や、
OS や ハードウェア自身も独自にアクセスします。

つまり、実時間(RTC)にアクセスする経路は無数に存在するということです。

 

上記より、64bit OS を搭載していたとしても int は 32bit 固定ですし、32bit アクセスも可能です。
例えば、以下のケースが経路中に存在すると、問題となる場合があります。

  • 64bit OS 上で 32bit のプログラムを実行(Multi-Arch機能)
  • 古い Runtime を使用している
  • 32bit 時代の OSS を TTIME64 というような configure を無効にしてビルド
  • INT_MAX や (1<<31) などを使って値域チェックを行っている
  • time_t を int で使用、またはキャストしている

 

対策案を考える【UNIX/Linux編】

対策案はいくつかありますが、影響範囲を考えると選択肢は限られます。
影響や実現性に関して、個別にみていきます。

 

64bit OS/ライブラリ を搭載

おそらく、このページにたどり着いた方は 32bit マイコンを使用していると想定しています。
64bit OS は搭載できませんので、本案は飛ばします。

 

過去の時刻をRTCに設定(オーバーフローさせない)

これが一番現実的な解になる方が多いと思います。
ただし、ソフトウェア層で RTC 情報の IN/OUT を全ておさえ、「完全な過去の世界」を作れることが条件。

コード量的に、独自に開発したソフトウェアの方が圧倒的に少ないと予想されるため、
オーバーフローさせないようにすることが一番影響範囲を抑えることができます。

SANACHAN
SANACHAN
ただし、延命措置にすぎないため、根本対策は必要になります。

 

具体的には、NTP や CMOS からの RTC 情報を誤魔化し、内部は過去の時間で動作させます。
外部との通信等で時刻を出力する場合は、そのプロトコルに合った時刻に補正します。

参考

2038年問題とは別に、NTP には「2036年問題」があります。
RFC2030 に記載の通り、「2036年以降の時刻に読み替える」必要があるので、ご注意ください。

 

また、対策事例もいくつか報告されているため、本案が最も実現性高そうです。

 

オーバーフローしたら2038年以降の時刻に読替え

NTP と同じ考え方の案です。

しかし、この案の採用には先ず、オーバーフローしても大丈夫なことの確認が必要になります。
本末転倒ですので、飛ばします。

 

time_t 型を 64bit 化

time_t の宣言は、すべてのソフトウェアが参照する Kernel のヘッダーで定義されています。

全てのソフトウェアを再ビルドし、再試験し・・・という影響範囲が膨大となります。
また、そのあとに 32bit アクセスしている箇所がないかを点検する必要があり実現性が低いでしょう。

 

基準時刻「1970年」を変更

こちらも Kernel の変更が必要となります。
ヘッダーというレベルではなく、ロジックの変更を伴います。
また、1970年を基準と意識したロジックを全て見つけ出し、その場所の修正も伴います。

影響範囲がソフトウェア全体となるため、現実的ではないでしょう。

 

Linux Kernel v5.4 以降に更新

32bit Linux で、v5.4 以降であれば本問題の対策が施されています。
先の「過去時刻を設定」が実現できない場合、次の有力候補となります。

glibc の更新も伴うため、ライブラリ群は軒並み更新が必要となります。
また、開発したアプリケーションも見直しが必要になるでしょう。

影響範囲は広いですが、無数の点検を行うより圧倒的にゴールできる可能性が高い案です。

参考

こちら「2038年問題への対応」で紹介されています。

 

Linux に導入された変更をマージ

おそらく v4 系以前の Linux を使っているのではないでしょうか。
だとすると、メジャーバージョンが異なるため、マージはほぼ不可能でしょう。

また、数千件の変更が毎月実施される Linux Kernel の履歴から、
該当するコミットを全て見つけ出すことは至難の業です。

 

対策案を考える【Windows編】

Windows の場合、99% 以上がブラックボックスであり、ソースコードが隠蔽されています。
点検するすべがありません。

しかし、Wikipedia では以下のような記載があり、よほど古いOSやRuntimeを使用していない限り
問題とはならず、問題ある場合はOS/Runtimeの更新しか手がないようです。

32bit版のWindows (Win32) では内部時刻の表現に64bit化された構造体を使っており、旧OSに関する一部のケースを除き、32ビット版であっても2038年問題は発生しない
ただし、Microsoft C/C++ (MS-C) および古いバージョンのMicrosoft Visual C++ (MSVC) においては、32bitを使って定義されていたため、これらの古い処理系のC/C++ランタイムライブラリ (CRT) を利用して構築されたアプリケーションやDLLなどは、2038年問題を抱えていることになる
x64アーキテクチャの64bit版Windows OS上では32bitアプリケーションも動作させることができるが、古い32bitアプリケーションにおける2038年問題は、たとえWindows OSを64bit版に入れ替えたとしても回避することはできない

SANACHAN
SANACHAN
独自に開発したアプリケーション等は含まれませんので、別途、点検は必須です。

 

おわりに

ソフトウェアは「果物」です。
保守を怠れば腐っていき、資産価値は「ゼロ」になります。

リファクタリング等、定期的にアーキテクチャや実装を見直す習慣をつけましょう。

 

こちらの記事もよく読まれています

  • この記事を書いた人
  • 最新記事
SANACHAN

SANACHAN

「生涯一エンジニア」を掲げ、大手グローバル企業でSE/PGとして8年勤め、キャリアアップ転職した現役のエンジニアです。世にあるメジャーな全プログラム言語(コボル除く)を自由に扱えます。一児の父。自分のため、家族のため、日々勉強してます。システムエンジニア、プログラミングに関する情報を蓄積している雑記帳です。

-OS
-, , ,