glibc在默認情況下並未妥善解決Y2038問題
由於Y2038 問題, Linux Kernel 早在幾年前就已經切換到64 位time_t,而且發行版Alpine 3.13 時也跳到了64 位time_t。不過近日,Alpine 安全團隊主管Ariadne Conill 發文表示GNU libc 2.34 在支持64 位time_t 上存在缺陷,可能在過渡過程中產生障礙。
Alpine 安全團隊主管Ariadne Conill,圖片來自於Twitter
如果你密切關注Linux 領域的發展和動向,肯定了解2038 年錯誤(Year2038 bug)。這個問題之所以會存在,是由於到了2038 年1 月19 日那天,可以用Unix 帶符號的32 位整數時間格式表示的最新時間是03:14:07 UTC。
可以用Unix 帶符號的32 位整數時間格式來表示的最新時間是2038 年1 月19 日03:14:07 UTC,這是1970 年1 月1 日之後過了2147483647 秒。過了那個時間後,由於整數溢出,時間值將作為負數來存儲,系統會將日期讀為1901 年12 月13 日,而不是2038 年1 月19 日。
Alpine 操作系統是一個面向安全的輕型Linux 發行版。它不同於通常Linux 發行版,Alpine 採用了musl libc 和busybox 以減小系統的體積和運行時資源消耗,但功能上比busybox 又完善的多,因此得到開源社區越來越多的青睞。
在博文中,Conill 表示在Alpine 使用的musl C 庫以及許多其他UNIX C 庫實現中,time_t 在新代碼中總是64 位的,並且為需要舊的32 位函數的代碼提供了兼容性存根(compatibility stubs)。當代碼隨著時間的推移被重建時,它將自動成為符合Y2038 標準的代碼,而無需任何努力。
微軟在msvcrt 中又前進了一步:你默認得到64 位的time_t,但如果你在編譯時定義了_USE_32BIT_TIME_T 宏,你仍然可以訪問舊的32 位函數。需要注意的是,上面描述的兩種方法都引入了零摩擦,以獲得正確的東西。
GNU 項目為過渡到新的ABI 所採取的方法正好相反:你必須明確要求新的功能,否則你將永遠得不到它。這種方法為將來改變默認值留下了可能性,但到目前為止,他們從未這樣做過。
這可以從大文件支持擴展中觀察到,需要處理大於2GiB 的文件,你必須總是用-D_FILE_OFFSET_BITS=64 來構建你的代碼。同樣,如果你在一個32 位系統上,而你沒有用-D_TIME_BITS=64 來構建你的應用程序,它將不會使用符合Y2038 的ABI 來構建。
這是最糟糕的方式。考慮一下庫:如果一個依賴關係是用-D_TIME_BITS=64 構建的,而另一個依賴關係沒有,並且它們需要相互交換結構timespec 或類似的東西,怎麼辦?那麼,在這種情況下,你的程序很可能會崩潰或出現奇怪的行為,因為你在編譯的程序中沒有持續使用相同的結構timespec。
幸運的是,如果你的目標是32位系統,而且你想處理大於2GiB的文件,或者有信心你的代碼在2038年還能繼續工作,有一些Linux發行版是建立在Musl之上的,比如Alpine,這將使你不必處理GNU libc的特殊性