Testowanie problemu roku 2038 w Windows
O 03:14:07 UTC 19 stycznia 2038 kończy się 32-bitowy licznik sekund od 1970 roku ze znakiem. Zegar Windows biegnie dalej - programy, formaty plików i biblioteki, które zachowały własną 32-bitową kopię, nie. Tak znajdziesz je w jednej aplikacji, bez ustawiania daty całej maszyny na rok 2038.
Gdzie ten błąd może jeszcze siedzieć
Zegar Windows to 64-bitowy licznik kroków po 100 nanosekund liczony od 1601 roku, więc nie
ma w nim roku 2038. Typ time_t w bibliotece uruchomieniowej C jest domyślnie
64-bitowy od Visual Studio 2005. Zostaje kod, który wybrał 32-bitowy czas albo po drodze
zgubił jego górną połowę:
- Kompilacje 32-bitowe z
_USE_32BIT_TIME_Talbo wywołujące wprost_time32i__time32_t. Dokumentacja Microsoftu ostrzega, że taka aplikacja może przestać działać po 18 stycznia 2038. - 64-bitowy czas zapisany w
intalbolong. W Windows oba mają 32 bity, nawet w programie 64-bitowym. - Biblioteki, formaty plików, protokoły sieciowe i kolumny w bazach danych, które trzymają sekundy od 1970 roku w 32 bitach.
- Kod, który dodaje lata. Dwudziestoletnia licencja, trzydziestoletni harmonogram kredytu albo certyfikat ważny do 2040 przekraczają tę granicę na długo przed nadejściem roku 2038.
Przekroczenie granicy w jednej aplikacji
Granica jest zdefiniowana w UTC, a --at czyta się w strefie sesji, więc przy
ręcznie wpisanej chwili ustaw strefę na UTC. Gotowy scenariusz niesie już bezwzględną chwilę
UTC i strefy nie potrzebuje.
# Scenariusz: sesja startuje na samej granicy, 03:14:07 UTC chrono run "C:\apps\test.exe" --preset year-2038 --report session.txt # Start siedemnaście sekund przed granicą - aplikacja przekracza ją na żywo chrono run "C:\apps\test.exe" --at 2038-01-19T03:13:50 --zone +00:00 --report session.txt # Start wcześniej i tempo 60 razy szybsze - granica nadchodzi po około 14 sekundach chrono run "C:\apps\test.exe" --at 2038-01-19T03:00:00 --zone +00:00 --mode x60 # Albo praca na dzisiejszej dacie i skok przez granicę, gdy aplikacja jest otwarta chrono run "C:\apps\test.exe" --jump-after 5:2038-01-19T03:14:05 --zone +00:00
Sekundę za granicą 32-bitowy licznik ze znakiem staje się -2 147 483 648, co czyta się jako 13 grudnia 1901, 20:45:52 UTC. Data z 1901 roku, liczba ujemna, pozycja, która nagle wygasła albo wylądowała na początku listy, wyjątek rzucony z kodu dat - tak przepełnienie wygląda z zewnątrz.
Ustawienie prawdziwego zegara na 2038 to drugi sposób, żeby się tu dostać, i ten psuje najwięcej. Co się dzieje, gdy zmieniasz datę systemową →
Sprawdzenie, że to dotarło do programu
Test, który przechodzi, bo program nigdy nie zobaczył 2038, jest gorszy niż brak testu. Raport na końcu każdej sesji mówi, który zegar program czytał i ile razy:
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)
Linia session potwierdza, że zegar aplikacji przeszedł granicę. Werdykt works z licznikami wywołań oznacza, że program czytał zegar i dostał datę z roku 2038; works partially wymienia kanały, które odczytał inną drogą. Co znaczą werdykty →
Czego zaliczony test nie dowodzi
- Przesunął się tylko zegar samego programu. Data wczytana z pliku albo z
bazy jest taka, jaka tam zapisana, więc data z roku 2038 w danych to osobny test.
Kalkulator dat wylicza takie daty za ciebie:
chrono calc --base 2038-01-19T03:14:07 --zone +00:00 --shift +1s - Serwer, z którym program rozmawia, nadal żyje w prawdziwej dacie. 32-bitowy limit po tamtej stronie jest poza zasięgiem, a sesja ostrzega, gdy widzi, że program otwiera połączenie sieciowe.