概要
- Rustプログラムで 未定義動作 発生
- from_raw_parts によるダングリング参照
- Box の解放後にメモリアクセス
- 問題箇所: PathString::slice
- 安全なコード設計の重要性
Rustにおける未定義動作と危険なパターン
- from_raw_parts は生ポインタからスライスを生成
- Box::new で確保したメモリを drop で解放
- 解放後のポインタを使い続けると ダングリング参照 発生
- これはRustでも 未定義動作 (Undefined Behavior, UB)に該当
- UBが発生すると、予測不能な動作やクラッシュ、データ破損の危険
問題の具体的なコードパターン
- main 関数内でBoxの所有権を持つ変数を生成
- その参照やポインタを PathString::init に渡す
- drop でBoxのメモリを解放
- 解放後に init.slice() で元のメモリへアクセス
- これにより dangling pointer (ダングリングポインタ)が生じる
安全なRustコード設計のポイント
- 所有権 と ライフタイム を明確に管理
- メモリ解放後のポインタや参照の使用禁止
- 生ポインタ操作や unsafe ブロックは最小限に留める
- from_raw_parts の利用は、メモリの有効期間が保証されている場合のみ
- &[u8] や String など安全な型でデータ管理
修正案と推奨パターン
-
Box や Vec など、所有権とライフタイムが自動管理される型を活用
-
構造体に &[u8] を保持する場合、元データの所有権またはライフタイムも合わせて管理
-
どうしても生ポインタを使う必要がある場合は、 drop やスコープに注意
-
例: PathString にBox自体を持たせる設計に変更
- 例:
struct PathString(Box<[u8]>); impl PathString { fn slice(&self) -> &[u8] { &self.0 } }
- 例:
Rustで安全なコードを書くための心得
- 未定義動作 を防ぐため、所有権とライフタイムの正しい理解
- unsafe の使用は最小限、かつ十分な検証と解説コメントを添付
- コンパイラやClippyの警告を無視しない
- コードレビューやペアプログラミングで安全性を確保
- Rustの 安全性設計思想 を守ることが品質向上への近道
まとめ
- Rustは 安全性 が大きな特徴
- しかし unsafe や生ポインタ乱用で危険なバグ発生
- 所有権 と ライフタイム を守ることが最重要
- 安全なRust設計を心がけることが、バグや未定義動作の防止につながる