Rust 的邊界檢查實際費用是多少?

https://blog.readyset.io/bounds-checks/

Rust 的範圍檢查到底要花多少成本?

最近,OpenSSL 專案中報告了一對重要的漏洞。毫不奇怪的是,這兩個漏洞的根本原因都是緩衝區溢出,可以被攻擊者以惡意載荷觸發,造成崩潰和拒絕服務。可預見,許多 Rust 倡導者(我是其中之一)指出這正是 Rust 可以在靜態上預防的漏洞類型,並且清楚地展示「用 Rust 重寫它」可以帶來實質利益。

撇開 OpenSSL 等規模大、複雜且長期存在的專案從頭重寫的可行性或適當性,值得討論的是 Rust 究竟能夠如何防止這些緩衝區溢出漏洞。具體而言,由於 Rust 並非一種完全依賴於型別的語言,無法在編譯時證明我們的緩衝區的長度,它會採用運行時的範圍檢查來確保索引總是安全的。實際上,這意味著每當你對片段進行索引時,Rust 編譯器將發射一系列指令來檢查你的索引是否在該片段的範圍內,如果不是,則會發生 panic。

在 C 程式設計師看來,這可能聽起來像是 Rust 的缺點- 實際上在現實世界的程式碼中有許多地方,你確實可以靜態知道你的索引是在範圍內的,或者你有多個索引操作需要索引同一個片段,這樣一個限制索引的檢查便能涵蓋所有索引操作。總的來說,它很可能不是一種零成本的抽象,至少不符合 Bjarne Stroustrup 原始定義的:

你使用的內容,你無法手動編寫得更好。

不過,這多餘的範圍檢查究竟有多少成本呢?這裡有些先前的經驗-例如這個存儲庫包含了使用安全範圍檢查版本和不安全、非範圍檢查版本對片段進行緊密循環的基準測試。結論是,在這種情況下,範圍檢查通常只在其存在阻礙了像是自動向量化等優化時才會造成成本(這在實際應用中可能是一項極大的優化),但否則基本上是差不多。另一方面,我找不到對於具有高性能敏感度的實際、大型、生產 Rust 代碼庫中濫用範圍檢查的成本的深入分析,而我恰巧工作在其中一個這樣的庫上,所以我覺得檢視範圍檢查在熱路徑上的成本可能是有趣的。

在進行這次實驗之前,我將「熱路徑」定義為一個熱(在記憶中緩存)讀取單個查詢,包括緩存讀取成本本身和 SQL 協議轉換層。

範圍檢查發生的頻率如何?

在檢查範圍檢查的運行時成本之前,我想知道我們在熱路徑中有多頻繁地進行這些運行時範圍檢查。在試圖找出如何使用 perf 或 dtrace 進行這種工具的過程中,我發現了這個 StackOverflow 問題,解釋了如何使用 gdb 記錄特定代碼行被執行的次數:只需在該行上設置斷點,忽略下一百萬次或更多次觸發斷點,然後執行程式。然後,信息於斷點將會顯示斷點被觸發的次數!

(繼續)

via Rust on Medium

January 26, 2024 at 10:52PM

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *