RFC-0119:系統判定為有害的路徑系統

RFC-0119:系統絕對路徑經認定有害
狀態已接受
區域
  • 建構
說明

制定政策,盡可能使用相對路徑。

問題
變更
作者
審查人員
提交日期 (年/月)2021-06-20
審查日期 (年/月)2021-07-28

摘要

  1. 制定政策時,請優先使用相對路徑或來源絕對路徑,而非數個列舉執行個體的系統絕對路徑,並留意日後的用途。
  2. 改為以自動化方式強制執行這項政策,建立迴歸停止。
  3. 清除系統絕對路徑的既有用法。

背景

如果讀者熟悉該主題,可以略過這部分

路徑

定義

讀取器應熟悉路徑概念。

我們使用下列定義:

  • 系統絕對路徑:本機檔案系統根層級的路徑。通常以 / 前置字串表示。
  • 來源絕對路徑或專案絕對路徑:相對於來源樹狀結構或專案結帳的相對路徑。通常以 // 前置字元。
  • CWD 相對路徑:相對於目前工作目錄的路徑。

以下將我們指稱系統絕對路徑做為絕對路徑,將其他路徑稱為相對路徑,因為這些路徑會以相對路徑的形式表示。

Fuchsia 建構系統中的路徑

路徑可在建構系統中使用,以參照動作的輸入和輸出檔案。Fuchsia 建構系統會使用 GN 定義其建構圖表。有已建立的最佳做法,偏好將 GN 中的路徑繪製為相對於其他根目錄,例如根建構目錄或來源根目錄。

產生的程式碼中的路徑

程式碼可能基於多種原因而參照路徑。簽入的原始碼無法參照絕對路徑,因為其無法移動 - 這些路徑在修訂佇列 (CQ) 機器上並不合理,因此會遭拒;即使相同的絕對路徑在其他工程師的機器查看相同來源時,這些絕對路徑也不會合理。不過,在特定機器上產生且未檢查的程式碼可能包含絕對路徑,且仍可正常運作 (成功建構和/或執行)。

Fuchsia 利用許多工具產生原始碼。例如 FIDL 使用 fidlc,而 Banjo 使用類似的工具。

提振精神

基於以下幾個原因,您可以考慮在建構系統、工具叫用、產生的原始碼和其他成果中,優先使用相對路徑來使用絕對路徑。

可攜式構件

如上所述,相對路徑具有可攜性,因為它是相對於雙方可以同意的參考點,例如 Fuchsia 來源結帳的根目錄,或建構輸出目錄的根目錄。在許多情況下,建議您不要只偏好遷移路徑。

分散式建構的可攜式構件

發布建構動作是指將輸入內容傳送至建構動作 (例如將 C/C++ 檔案編譯至物件檔案) 至遠端伺服器來執行。遠端伺服器可能會代表用戶端執行動作並傳回結果。遠端伺服器通常會依賴內容型快取來完全略過動作。

發布建構動作有許多優點,但並不在本文件的範圍內。

由於用戶端和伺服器可能不會同意絕對路徑,因此在指定要發布的叫用詳細資料時,可能需要相對路徑,並指出這些參照彼此參照的任何已上傳構件內容。這對於仰賴快取的分散式建構系統而言更是如此,因為其中叫用詳細資料會做為快取金鑰的一部分。即使允許絕對路徑,使用也會使快取機制失效,因為兩個用戶端可以檢查相同的原始碼,然後再將要求傳送給絕對路徑中不同的伺服器。

建構路徑或產生的程式碼中存在絕對路徑,會導致之前分散式建構發生問題。舉例來說,Fuchsia 開發人員可以使用 Goma 發布 C/C++ 建構動作。Goma 使用者的 Fuchsia 使用者在透過 C/C++ 使用絕對路徑 (包括目錄) 的變更推出前,曾遇到服務中斷的情況。在某些執行個體中,分散式建構作業會失敗並強制執行速度較慢的本機回退。在其他執行個體中,分散式建構可能會成功,但無法達到快取,進而導致後端負載量大幅增加,進而導致連鎖性故障。

發布建構動作時,另一個類似的故障模式是工具叫用中的絕對路徑。我們先前「發現」在發布動作前檢查這類路徑,並且以建構動作失敗和實用錯誤的形式拒絕這些路徑,是很實用的做法。

其他執行個體的絕對路徑中,會產生建構正確性問題。

管線的可攜式構件

分配動作時,有時希望能擁有遠端伺服器管道來處理不同的工作。舉例來說,有些機器可能更適合執行建構作業,其他則更適合用於執行建構的測試。有時候,動作會以建立分支和彙整的圖形表示,例如,建構一套測試套件,然後建立多台機器,而每個機器都會執行部分測試,然後彙整結果。

在先前的案例中,用戶端與伺服器之間交換路徑時,會在管道中的不同伺服器之間交換路徑。儘管如此,但無論交換的性質為何,相對路徑仍適用相同原因。

絕對路徑可能會導致管道中斷。舉例來說,過去在早期階段產生的涵蓋率報告包含原始碼的絕對路徑,而在先前階段的涵蓋率報告生產管道中,產生測試涵蓋率的管道會對之前產生測試涵蓋範圍的管道中斷。產生涵蓋率對應檔案的工具使用絕對路徑,而我們「已變更」以將指定基本路徑轉換為指定基本路徑。

使用其他形式的程式碼檢測 (也就是在偵錯資訊檔案中使用絕對路徑) 時,也會發生類似的故障,用於解析原始碼行相對電腦偏移值的記錄。

用於快取的可攜式構件

系統可能會快取建構輸出,以加快後續建構的速度,稱為漸進式建構。這類快取通常會儲存在本機,例如開發人員工作站或建構伺服器的特定執行個體。在理論中,不同的建構工作站 (工作站和伺服器) 也可以交換各個建構工作站 (工作站和伺服器),前提是網路容量可以滿足這種情況,且不會有任何安全性和隱私權疑慮。

Fuchsia 目前不會重複使用不同機器之間的建構快取,因為已知某些建構輸出內容包含絕對構件。因此,Fuchsia 開發人員和 Fuchsia 分散式建構工具,最多會使用自己先前執行的版本中自身的本地化快取。這大幅錯失了最佳化和工程工作效率的機會。

可重現的構件

在構件中使用絕對路徑,我們無法達成可重現的建構作業

可重現的建構目前不是 Fuchsia 的明確目標。然而,瞭解可重現的建構的優點在於:瞭解這日後可能是所需的屬性,而且在構件中使用絕對路徑將使我們無法達成可重現性。

另請注意,其他構件非可重現來源,大多是在這個 RFC 的範圍內。

極少工作

Fuchsia 目前會產生完整的系統映像檔並設定裝置,藉此在裝置上執行測試。我們日後可能會希望加速這項程序,例如只推送至測試裝置,就可藉由推送至上次更新後變更的 blob。大多數變更都只會對與基礎變更相對的少數 blob 造成影響,因此這種方法能大幅加快測試裝置的速度。

如果絕對路徑外洩到構件,則在要測試的變更之間,可能會使更多 blob 失效,並非絕對需要。

紫紅色外牆建築

在此,我們將說明 Fuchsia 因絕對路徑而遇到的一些問題,以及絕對路徑會阻礙 Fuchsia 難以改進及改善的部分。從歷史背景來看,解決絕對路徑問題是迫切感的關鍵。例如,過去 Fuchsia 並未使用漸進式建構或快取,因此專案及其參與的人員會學會處理缺陷,從而讓 Fuchsia 停止採用更多漸進式建構作業和快取。

如果 Fuchsia 成功,其他專案就會使用 Fuchsia 的程式碼和構件,並為 Fuchsia 進行開發。請放心,至少將部分專案會預期建構規則和工具的系統,其版本和工具會更符合 Fuchsia 專案的需求。舉例來說,某些客戶可能會以大規模的方式運作,因為漸進式建構是必要性,而因此必須快取。如果 Fuchsia 嘗試達成這些屬性的阻礙,Fuchsia 開發人員和其他客戶就會遇到採用障礙。

分散式信任

如果建構的所有構件都能重現,這樣就可以開啟建構系統的新屬性大門。舉例來說,可重現的建構作業可以做為加密編譯信任鏈的分散式替代方案,用於驗證分散式二進位檔的完整性。不信任的各方只要嘗試從相同的來源和建構系統重現二進位檔,即可稽核這些二進位檔。如果無法重現二進位檔,則可能代表出現惡意竄改行為。

如果在構件中使用絕對路徑,不信任的方就無法重現相同的結果。

便利

相對路徑在疑難排解時更容易使用。預期一致性通常會出現比較一些,因此執行個體可以比較成功與失敗作業之間的路徑,並找出任何有意義的差異。

在完全本機工作時,絕對路徑非常方便。例如,您可以從工具叫用複製絕對路徑,並在不同的本機殼層環境中使用這類路徑,而且該路徑一定有效,因為其不會影響目前工作目錄的執行個體。此外,您還可以透過字串身分檢查更多絕對和正規化的兩個路徑 (例如:解析 ... 部分、遵循連結等)。由於任何路徑都可以設為絕對值和正規化,且這種轉換是冪等的,因此可為本機環境中的路徑提供簡單的對等檢查。不過,只要使用者發現這樣更方便,就不會限制在絕對路徑上執行這項轉換,但由於這種轉換相對於絕對和/或正規化形式,將無法從另一個方向執行。因此,更加多元包容所有用途,偏好使用相對形式。

設計

政策

我們會將記錄的 GN 最佳做法升級為一般政策,並擴大適用範圍,避免僅限於 BUILD.gn 檔案。我們特別建議您:

  1. 由建構系統以指令列引數傳遞至工具的路徑應與叫用工具的現有工作目錄 (以 root_build_dir 表示的 GN/Ninja) 相關。
  2. 建構期間產生的檔案路徑應相對於同一個根建構目錄。例如:產生的原始碼、套件資訊清單、depfiles
  3. 在執行階段產生的路徑應相對於專案來源根目錄。例如當機事件中的檔案資訊、測試涵蓋範圍報告。

違規處置

我們將導入新工具,根據工具叫用和構件中的絕對路徑來淨化 Fuchsia 建構作業。這些工具將在 CQ 中執行,以避免發生迴歸問題。

清除

上述工具將有許可清單的功能,系統會將這個預設用途初始化,以列出所有現有的政策違規行為。系統會啟動清理工作,將許可清單大小縮減為零。在正常情況下,迴歸不會列入許可清單。

實作

實作詳細資料說明強制執行工具的運作方式不會上升至 RFC 等級。不過,我們在下方列舉了一些想法的草圖,以滿足好奇讀者的需求。

正在清理 Ninja 檔案

執行 GN 會產生描述建構圖表的 build.ninja 檔案。這張圖表的說明包含所有工具叫用,包括要叫用的工具路徑,以及系統以引數形式傳遞至這些工具的路徑。使用的其他檔案會在「depfile」中參照。

您可以使用 strings 處理這些檔案,產生符記,之後就能以絕對路徑的外觀進行篩選。您可以將這個簡易掃描工具實作為主機測試,可依據所有建構變數執行。

清除建構作業參照的檔案

除了清理 Ninja 檔案外,我們還可將指定做為輸入或輸出的任何檔案權杖化和清理,以便建構動作。假設該版本是密封技術,我們可以從 Ninja 圖表或解碼檔案探索所有這類檔案。

檢查是否含有絕對路徑的字串

我們可以掃描建構輸出目錄 (out/) 下的所有檔案,產生字串,並檢查是否有任何是絕對路徑,然後發出錯誤。這並不是絕對的保護,而是額外的簡單防線。

在動作追蹤工具中拒絕絕對路徑

我們已有包裝 GN 動作的工具,並且已經使用該工具拒絕檔案中的絕對路徑。我們也可以進一步擴展這項機制。

請注意,我們目前只會使用動作追蹤器包裝自訂動作,這部分是所有建構動作的子集。我們採取的做法是相同的效能問題從這個角度來看,或許進一步運用動作追蹤並不是全方位的策略。

撤銷絕對路徑

如要保持結帳中的所有相對路徑正常運作,同時完全無效絕對路徑,最簡單的方法是產生 Ninja,然後將結帳目錄移至其他位置 (或直接重新命名),然後再建構。

$ fx gen
$ mv $FUCHSIA_DIR ${FUCHSIA_DIR}_renamed
$ fx build

如果結帳下方任何建構叫用參照路徑為絕對值,建構作業就會失敗。

這個做法非常容易實作,且具備可攜性,且沒有效能負擔。部分缺點包括,在發生故障時錯誤訊息,會讓未啟動的錯誤訊息感到困惑,而在產生的構件中 (例如依附元件、srcgen、偵錯資訊) 仍有可能外洩的絕對路徑。

執行階段環境變更

另一個做法是變更版本的執行階段環境,使絕對路徑轉譯無效或轉譯時會造成損害。例如,部分專案會使用 chroot 等概念,在結帳根層級組成沙箱。其他建構系統使用特殊檔案系統來做沙箱作業。

執行階段方法具有更強大的正確性保證,因此值得考慮。不過,您需要考量到效能問題和具挑戰性的可攜性 問題

安全性考量

當路徑的解譯 (實際解析的檔案) 可能會影響敏感系統行為時,路徑可能會用於這類位置。例如,可以將可能對應到記憶體的檔案許可清單做為執行檔頁面。

在這種情況下,最好使用專案相關路徑,比起含有指定清單的目錄之執行個體路徑,或是與處理此清單的工具 CWD 相對的路徑。這是因為在定義專案相關路徑時,專案相關路徑的解析度不明確,其他形式的相對路徑解讀方式可能因全域可變動狀態 (例如 CWD) 而有不同的解讀。

隱私權注意事項

絕對路徑有時可能會洩漏個人身分識別資訊。舉例來說,使用者名稱通常出現在絕對路徑的一部分,該路徑包含該使用者的結帳或建構輸出目錄中的檔案。將絕對路徑替換為來源相關路徑,就不會造成 PII 發生問題。

測試

先前我們探討了幾種實作檢查使用絕對路徑的方式。由於這些檢查是以建構步驟或主機測試的形式實作,因此可以在 CQ 中執行,並做為持續測試使用。

要確保不使用絕對路徑的另一種方法,是將其狀態調整為可以容許工程工作流程的重要部分。舉例來說,如果絕對路徑破壞了已發行的特定動作,且該動作為 CQ 的一部分,那麼開發人員就不能再導入中斷情形。

說明文件

如果強制執行該路徑的工具未絕對失敗,則該工具應產生錯誤,並連結至適當的疑難排解頁面。在此提醒您,當強制執行建構動作密封的動作追蹤器時,會產生錯誤訊息,並且提供密封建構動作頁面的連結。

缺點、替代方案和未知

萬一出現分散式建構空間和可重現性空間的機會,我們不採取任何行動。

我們可以製定政策,但不會採取行動來強制執行。這可能導致無法在分散式建構或可重現性方面取得有意義的進展。

我們可以排除這個問題,但代價是,會隨著時間累積額外的迴歸,

先前的圖片和參考資料

一位優秀的絕地武士曾說:「只有十次特惠。」