camlp5 的緊急問題
升級至 OCaml 4.06.1 時,opam-repository camlp5 7.03 的封裝問題
在 2017 年 10 月 26 日至 2018 年 2 月 17 日期間,opam-repository 中 camlp5 7.03 的 OPAM 套件在某些情況下能夠在 macOS 和其他預設不阻止遞迴根目錄刪除的系統上觸發 rm -rf /
。本文包含關於如何識別您的 OPAM 安裝是否受到影響以及如何修復它的建議。
簡而言之,如果 rm --preserve-root
給出的訊息是類似於 unrecognised option
而不是 missing operand
,並且您正在運行 OPAM 1.2.2,請確保在將您的系統編譯器升級到 OCaml 4.06.1 **之前** 運行 opam update
。如果您已經將 *您的系統編譯器* 升級到 OCaml 4.06.1 (例如,使用 Homebrew),請繼續閱讀。
識別您是否受到影響
如果以下三件事都成立,您將面臨刪除所有檔案的嚴重風險
- 您的系統
rm
命令不支援--preserve-root
預設值(您可以透過運行rm --preserve-root
並注意錯誤訊息是指「unrecognised option」還是「missing operand」來識別) - 您的系統 OCaml 編譯器是 4.06.1,並且您正在使用 OPAM 1.2.2
- 您在 2017 年 10 月 26 日之後與 opam-repository 同步,但自 2018 年 2 月 18 日以來沒有同步過
如果您的系統受到影響,則大多數 OPAM 命令都無法運行。特別是,如果 OPAM 詢問
dra@bionic:~$ opam update
Your system compiler has been changed. Do you want to upgrade your OPAM installation ? [Y/n] n
您必須對此問題回答否.
我編寫了一個腳本,可以安全地識別您的系統是否受到影響,可以在 GitHub 上查看,或直接執行
$ curl -L https://raw.githubusercontent.com/dra27/opam/camlp5-detection/shell/opam-detect.sh | sh -
此腳本會掃描 $HOME
所識別的目錄,以尋找任何看起來像 OPAM 根目錄的內容。幾乎所有使用者都會在 ~/.opam
中有一個 OPAM 根目錄,如果您不知道如何使用多個根目錄運行 OPAM,那麼您可能沒有其他需要擔心的根目錄!
腳本可能會顯示各種訊息。如果您的系統至少包含一個受影響的 OPAM 1.2 根目錄,您將看到類似這樣的輸出
dra@bionic:~/opam$ shell/opam-detect.sh
opam 1.2.2 found
Scanning /home/dra for opam roots...
opam 1.2 root found in /home/dra/.opam
camlp5 is faulty AND installed AND the system compiler is OCaml 4.06.1
THIS ROOT CANNOT BE UPDATED OR UPGRADED. DO NOT ALLOW OPAM TO UPGRADE THE SYSTEM
COMPILER. DOING SO WILL ATTEMPT TO ERASE YOUR MACHINE
Please see https://github.com/ocaml/opam/issues/3322 for more information
修復它
在所有情況下,一種修復方法是安裝 opam 2 的最新候選版本,並將您的 OPAM 1.2 根目錄升級為 opam 2 格式。升級會阻止 OPAM 1.2.2 讀取根目錄。如果您收到上述訊息,並選擇升級到 opam 2(升級根目錄最簡單的方法是在安裝 opam 2 後運行 opam list
),然後再次運行 opam-detect.sh
腳本。和以前一樣,**如果被問到「您的系統編譯器已變更」,請勿回答「是」**。
如果您不想升級到 opam 2,並且有許多充分的理由不想這樣做,則還有其他兩種可能性。最簡單的方法是將您的系統編譯器降級回 4.06.0(或更早的版本)。然後,您可以再次運行 opam-detect.sh
並檢查錯誤訊息。只要訊息不再是上面的訊息,您就可以運行 opam update
以更新交換器上的儲存庫元數據。然後,您可以再次將您的系統編譯器升級回 OCaml 4.06.1。為了絕對確定,您可以再次運行 opam-detect.sh
腳本,並假設訊息仍然不是上面的訊息,然後您可以允許 OPAM 1.2.2 升級您的系統交換器。 *這是建議的行動方案。*
最後一種選擇是您可以手動編輯 opam 根目錄,並欺騙 opam 相信 camlp5 套件不再安裝。這是透過編輯根目錄內的 system/installed
檔案並刪除 **camlp5** 行和任何依賴 camlp5 的套件(例如,coq
)來完成的。您無法在此階段使用 opam
來確定依賴項,因此您需要使用線上索引來檢查依賴套件。如果您未能刪除所有依賴 camlp5 的套件,OPAM 將顯示類似這樣的安裝提示
dra@bionic:~$ opam update
Your system compiler has been changed. Do you want to upgrade your OPAM installation ? [Y/n] y
=-=- Upgrading system -=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
The following actions will be performed:
∗ install camlp5 7.03 [required by coq]
∗ install conf-m4 1
∗ install base-threads base
∗ install base-unix base
∗ install base-bigarray base
∗ install ocamlfind 1.7.3
∗ install num 1.1
∗ install coq 8.7.0
===== ∗ 8 =====
Do you want to continue ? [Y/n]
如果發生這種情況,請回答否,但在這個階段,您的系統交換器將被清空所有套件(您現在可以安全地運行 opam update
了)。當然,您可以在嘗試此操作之前備份 OPAM 根目錄。升級後,您可以運行 opam update
並重新安裝遺失的套件。**不建議採取此行動方案,因為 opam-detect.sh
腳本將不再有幫助。強烈建議您在嘗試此解決方案之前備份檔案**。
問題
在 2017 年 10 月 26 日,合併了 PR#10523,其中封裝了 camlp5 7.03。這是第一個發佈到 opam 的 camlp5 版本,它支援 OCaml 4.06.0。
不幸的是,這也是 opam 套件中第一個包含執行 make uninstall
的 remove
區段的版本。該套件還包含不正確的 available
約束 - 它應該只允許來自 4.06 分支的 OCaml 4.06.0,但給定的約束允許所有版本。
camlp5 的 configure
腳本負責使用所有常用的配置設定(包括 PREFIX
等)編寫 config/Makefile
。此腳本包含 OCaml 的版本檢查,如果版本不受支援,則會失敗。不幸的是,即使它失敗,它也會寫入部分的 config/Makefile
以啟用某些開發目標。遺憾的是,這使得 uninstall
目標中的命令 rm -rf "$(DESTDIR)$(LIBDIR)/$(CAMLP5N)"
中的三個變數都未定義,從而留下了肯定不必要的 rm -rf /
。
自 2003 年 11 月(在 5.1.0 版本中)以來,GNU coreutils 的使用者預設設定了 --preserve-root
選項,這會導致 rm -rf /
引發錯誤。不幸的是,macOS 預設不使用 GNU coreutils。
在 OPAM 1.2 之前,opam
檔案的 build
和 install
區段是合併的。因此,如果 build
失敗,OPAM 將會靜默地執行 remove
命令,以便清除可能發生的任何部分安裝。儘管 OPAM 1.2 建議將 build
和 install
命令分開,但這並非強制性的,因此它保留了「靜默移除」行為。opam 2 強制分離(並且,如果可以使用沙箱,現在會強制執行)。opam 2 還期望 remove
命令在乾淨的原始碼樹中執行,對於此 camlp5 案例,這表示 **opam 2 使用者不會受到此問題的影響**。
OCaml 4.06.1 在 2018 年 2 月 16 日透過 PR#11433 新增至 opam-repository。在接下來的 48 小時內,有人注意到 camlp5 套件試圖執行 rm -rf /
(請參閱 Issue #11440),並且該套件在 2018 年 2 月 18 日透過 PR#11443 進行了修補。不幸的是,當時並未意識到 GNU coreutils 保護的重要性,並且還假設只有當您不幸在 OCaml 4.06.1 發佈到 opam-repository 與 opam-repository 中 camlp5 7.03 的修補之間更新了 OPAM 時(因此是 2018 年 2 月 16 日至 18 日),才會遇到此問題,並且 OPAM PR#3231 因此被認為非常不幸。
然而,真正的問題是將系統編譯器升級到 4.06.1,這在該 Issue 中沒有注意到,但在 Issue #3316 中正確識別出來。不幸的是,這為該問題提供了一個更大的時間範圍 - 如果您在 2017 年 10 月 26 日至 2018 年 2 月 18 日之間運行了 opam update
,並且從那以後沒有運行,那麼如果您的系統編譯器更新到 OCaml 4.06.1 而沒有先運行 opam update
,您的系統將會面臨風險。
如果系統編譯器發生變更,OPAM 1.2.2 在幾乎所有命令(包括 opam update
)上都會先要求升級 system
交換器。此步驟是強制性的,可防止 OPAM 1.2.2 的進一步安全使用。
未來緩解
由於 opam 2 處理套件安裝的方式發生了變更,opam 2 並未受到此情況的影響,但 opam 2 的主要開發人員 @AltGr 公開承認這更多是運氣而不是判斷。但是,opam 2 的第二個候選版本包括 Linux 和 macOS 上對沙箱的強制支援。沙箱套件建置和安裝將保護 opam 2 免受未來此類問題的影響,因為有故障的建置系統將無法在其建置目錄外部或在安裝期間切換根目錄上操作檔案。