プログラミング
Rustのborrow checkerで11年間未検出だった健全性に関するバグが発見される
11 year old unsoundness bugs found in Rust borrow checker (github.com)
要約
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