HN 日本語サマリー

← 一覧へ戻る
プログラミング

Rustのborrow checkerで11年間未検出だった健全性に関するバグが発見される

11 year old unsoundness bugs found in Rust borrow checker (github.com)

184 pointsby formerly_proven71 コメント

要約

Rustのborrow checkerにおいて、構造体のライフタイム要件が共変的なフィールド代入によって回避されるという深刻な健全性に関するバグが発見されました。この問題は、コンパイラが安全でないポインタを生成する可能性があり、長年(少なくとも1.18.0以降)存在していたにも関わらず、最近になって報告されました。このバグは、メモリ安全性を損なう可能性があり、高優先度で対応が進められています。

全文翻訳

構造体のライフタイム要件が共変的なフィールド代入によって回避される #164250 新規イシュー コピーリンク 新規イシュー コピーリンク オープン オープン 構造体のライフタイム要件が共変的なフィールド代入によって回避される #164250 コピーリンク ラベル A-borrow-checker エリア: ボロチェッカー エリア: ボロチェッカー A-destructors エリア: デストラクタ (`Drop`, …) エリア: デストラクタ (`Drop`, …) A-lifetimes エリア: ライフタイム / リージョン エリア: ライフタイム / リージョン A-variance エリア: バリアンス (https://doc.rust-lang.org/nomicon/subtyping.html) エリア: バリアンス (https://doc.rust-lang.org/nomicon/subtyping.html) C-bug カテゴリ: これはバグです。 カテゴリ: これはバグです。 I-unsound イシュー: 健全性の穴(最悪の種類のバグ)、参照: https://en.wikipedia.org/wiki/Soundness イシュー: 健全性の穴(最悪の種類のバグ)、参照: https://en.wikipedia.org/wiki/Soundness P-high 高優先度 高優先度 T-types 型チームに関連。PR/イシューのレビューと決定を行います。 型チームに関連。PR/イシューのレビューと決定を行います。 説明 jsnell 2026年10月13日にオープン イシュー本文 アクション 以下のコードで: use std::sync::Mutex; static KEPT: Mutex<Vec<Box<dyn std::any::Any + Send>>> = Mutex::new(Vec::new()); struct Q<T: 'static + Send>(Option<T>); impl<T: 'static + Send> Drop for Q<T> { fn drop(&mut self) { if let Some(t) = self.0.take() { KEPT.lock().unwrap().push(Box::new(t)); } } } fn main() { { let l = String::from("hello world, this is long enough"); let mut q = Q(None); q.0 = Some(&l); } let _z = String::from("XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"); let k = KEPT.lock().unwrap(); println!("{}", k[0].downcast_ref::<&'static String>().unwrap()); } 期待される結果: プログラムが受け入れられない 実際の結果: プログラムはコンパイルされ、「XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX」と出力されます。 asanを使用すると、プログラムはAddressSanitizer: stack-use-after-scopeでクラッシュします。 現在のnightlyおよび少なくとも1.85.0までの安定版でのメタイシュー asanありのGodbolt: https://rust.godbolt.org/z/83MvfbKde asanなしのGodbolt: https://rust.godbolt.org/z/GsGdqqMWeReactions リアクションは現在利用できません アクティビティ jsnell C-bugカテゴリ: これはバグです。 カテゴリ: これはバグです。 を追加しました 2026年10月13日 rustbot needs-triage このイシューはトリアージが必要な場合があります。完了したら削除してください。ドキュメント参照: forge.rust-lang.org/release/issue-triaging このイシューはトリアージが必要な場合があります。完了したら削除してください。ドキュメント参照: forge.rust-lang.org/release/issue-triaging を追加しました 2026年10月13日 theemathas I-unsound イシュー: 健全性の穴(最悪の種類のバグ)、参照: https://en.wikipedia.org/wiki/Soundness イシュー: 健全性の穴(最悪の種類のバグ)、参照: https://en.wikipedia.org/wiki/Soundness を追加しました 2026年10月13日 rustbot I-prioritize イシューはチームメンバーによる影響評価が必要です。P-{low,medium,high,critical}に置き換えられます。 イシューはチームメンバーによる影響評価が必要です。P-{low,medium,high,critical}に置き換えられます。 を追加しました 2026年10月13日 theemathas A-borrow-checker エリア: ボロチェッカー エリア: ボロチェッカー T-types 型チームに関連。PR/イシューのレビューと決定を行います。 型チームに関連。PR/イシューのレビューと決定を行います。 を追加し、needs-triage このイシューはトリアージが必要な場合があります。完了したら削除してください。ドキュメント参照: forge.rust-lang.org/release/issue-triaging このイシューはトリアージが必要な場合があります。完了したら削除してください。ドキュメント参照: forge.rust-lang.org/release/issue-triaging を削除しました 2026年10月13日 theemathas 2026年10月13日にコメントしました theemathas 2026年10月13日 コントリビューター その他のアクション 健全性のコア: struct Q<T: 'static>(Option<T>); impl<T: 'static> Drop for Q<T> { fn drop(&mut self) { // ここでのコードは、非'staticな&Stringにアクセスできますが、 // &'static Stringであると仮定しています。 } } fn main() { let l = String::new(); let mut q = Q(None); q.0 = Some(&l); } 👀 リアクション: 👀8 リアクションしたユーザー: Daniel Vogelbacher, Serhiy, BioTomateDE, Timur Tugushev, palapapa, Ori Yadid, SeniorMars, dzmitry-lahoda theemathas A-destructors エリア: デストラクタ (`Drop`, …) エリア: デストラクタ (`Drop`, …) を追加しました 2026年10月13日 rustbot I-prioritize イシューはチームメンバーによる影響評価が必要です。P-{low,medium,high,critical}に置き換えられます。 イシューはチームメンバーによる影響評価が必要です。P-{low,medium,high,critical}に置き換えられます。 を削除しました 2026年10月13日 rustbot 2026年10月13日にコメントしました rustbot 2026年10月13日 コラボレーター その他のアクション rustbot P-critical クリティカル優先度 クリティカル優先度 を追加しました 2026年10月13日 theemathas 2026年10月13日にコメントしました theemathas 2026年10月13日 コントリビューター その他のアクション 私のコードのバージョンは1.18.0(2017年)以来コンパイルされています。どうしてこれまで見つからなかったのでしょうか?? 👀 リアクション: 👀11 リアクションしたユーザー: Max, Alisa Sireneva, Leon, maxbry-uh, Luke, slaiyer, Screeching Bagel, Slanterns, maliciousblahaj, Timur Tugushev, 他1名 theemathas 2026年10月13日にコメントしました theemathas 2026年10月13日 コントリビューター その他のアクション 関連する可能性のあるイシュー: #128225 lcnr 2026年10月13日にコメントしました lcnr 2026年10月13日 コントリビューター 静的な構造体に依存しない場合 MyType<'a, 'b: 'a, 'c>(&'c mut &'a str, &'b str); impl<'a, 'b: 'a, 'c> Drop for MyType<'a, 'b, 'c> { fn drop(&mut self) { *self.0 = self.1; } } fn main() { let mut me_out = "rarw"; { let l = String::from("some cool string"); let mut q = MyType(&mut me_out, ""); q.1 = &l; } println!("me_out: {me_out}"); } 🚀 リアクション: 🚀1 リアクションしたユーザー: dzmitry-lahoda lcnr 2026年10月13日にコメントしました lcnr 2026年10月13日 コントリビューター 1.4から健全性が失われています🤣 https://rust.godbolt.org/z/j1s1TevhP 😄 リアクション: 😄15 リアクションしたユーザー: Rémy Rakic, D. Savchenko, Mathijs Follon, FelixKLG, Alex Gershberg, Kamilla 'ova, Tux3, slaiyer, Daniil Kondratev, Timur Tugushev, 他5名 rustbot P-critical クリティカル優先度 クリティカル優先度 を削除しました 2026年10月13日 rustbot 2026年10月13日にコメントしました rustbot 2026年10月13日 コラボレーター その他のアクション Zulipでの議論に基づき、P-highを割り当てます。 ❤️ リアクション: ❤️1 リアクションしたユーザー: Jeremy Lempereur rustbot P-high 高優先度 高優先度 を追加しました 2026年10月13日 theemathas 2026年10月13日にコメントしました theemathas 2026年10月13日 コントリビューター その他のアクション 別のバージョン、なぜダメなのか。おそらくこれが最も理解しやすいバージョンでしょう。 use std::time::Duration; use std::thread; struct Q<'a: 'static>(Option<&'a String>); impl<'a: 'static> Drop for Q<'a> { fn drop(&mut self) { // ここでのコードは、非'staticな&Stringにアクセスできますが、 // &'static Stringであると仮定しています。 let wrong: &'static String = self.0.unwrap(); thread::spawn(move || { thread::sleep(Duration::from_secs(1)); println!("{}", wrong); }); } } fn main() { { let l = String::from("abc"); let mut q = Q(None); q.0 = Some(&l); // q.0 は &'static String であるはずですが、非'staticな&Stringを代入しています。 } thread::sleep(Duration::from_secs(2)); } maxdexh 2026年10月13日にコメントしました maxdexh 2026年10月13日 メンバー その他のアクション これもセグフォールト/解放済みメモリの表示を引き起こす別のバージョンです。1.4から発生しています。 struct Q<T, F: FnMut(&T)>(Option<T>, F); impl<T, F: FnMut(&T)> Drop for Q<T, F> { fn drop(&mut self) { self.1(self.0.as_ref().unwrap()) } } fn main() { let mut out: Option<&'static _> = None; { let l = vec![vec![1]]; let mut q = Q(None, |s| { out = Some(*s); }); q.0 = Some(&l); } println!("{:?}", out); } lcnr タイトルを変更しました [-]構造体の`T: 'static`制約が共変的なフィールド代入によって回避される [+]構造体のライフタイム要件が共変的なフィールド代入によって回避される 2026年10月13日 maxdexh A-lifetimes エリア: ライフタイム / リージョン エリア: ライフタイム / リージョン A-variance エリア: バリアンス (https://doc.rust-lang.org/nomicon/subtyping.html) エリア: バリアンス (https://doc.rust-lang.org/nomicon/subtyping.html) を追加しました 2026年10月13日 theemathas 2026年10月13日にコメントしました theemathas 2026年10月13日 コントリビューター その他のアクション 別のバージョン、なぜダメなのか。このバージョンはおそらく最も理解しやすいでしょう。 use std::time::Duration; use std::thread; struct Q<'a: 'static>(Option<&'a String>); impl<'a: 'static> Drop for Q<'a> { fn drop(&mut self) { // ここでのコードは、非'staticな&Stringにアクセスできますが、 // &'static Stringであると仮定しています。 let wrong: &'static String = self.0.unwrap(); thread::spawn(move || { thread::sleep(Duration::from_secs(1)); println!("{}", wrong); }); } } fn main() { { let l = String::from("abc"); let mut q = Q(None); q.0 = Some(&l); } thread::sleep(Duration::from_secs(2)); } maxdexh 2026年10月13日にコメントしました maxdexh 2026年10月13日 メンバー その他のアクション 型の一致付けは、型のライフタイム制約を一般的にチェックしないと思います。通常の型の一致付けの例、フィールド代入なし: struct Q<T: Copy, F: FnMut(T)>(T, F); impl<T: Copy, F: FnMut(T)> Drop for Q<T, F> { fn drop(&mut sel