
2026/06/16 1:11
Rust と C/C++ のメモリ安全性に関する CVE の違い
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
Rust と C/C++ の脆弱性情報数を単純に比較してソフトウェアセキュリティを評価することは誤解を招くものであり、2 つの言語は「安全性」を根本的に異なる定義しているためです。C と C++ では多くの API が暗黙的に不安全であり、不適切に呼び出される場合(例えば、無効なポインタを渡す)にセグメンテーションフォルトなどのメモリエラーが発生します。此类の状態は通常、ライブラリの欠陥ではなく使用誤りとみなされますが、それでも CVE リポートが発生します。すべての乱用が正式に報告された場合、C/C++ ライブラリには未定義の動作を広く許容することから、数百万もの CVE が蓄積することになります。一方、Rust では
unsafe と明示的にマークされていない限りメモリ安全性が保証されており、unsafe ブロックのない Rust の脆弱性は「健全性の穴(soundness hole)」——つまり言語の中核的な安全性保証の違反——を示しています。したがって、C/C++ における CVE 数の多さは不良な設計を反映しているのではなく、寛容なエラー許容性を表しており、Rust では CVE の存在自体が健全性の侵害を意味します。将来のリスク評価では、単なる生じた脆弱性情報統計に頼るのではなく、コードが明示的な安全性マーカー(unsafe)に依存しているか、正しい使用に関する隠れた前提に依存しているかに焦点を当てるべきです。本文
Rust と C/C++: CVE とメモリエンスイティの扱い方の違い
本稿では、ソフトウェアのセキュリティ脆弱性情報である CVE の報告方法に焦点を当てます。特に「メモリエンスイティ(メモリ安全性)」に関連するバグについて、Rust と C/C++ でどのように処理が異なるかを詳述します。
背景と課題
- CVE はセキュリティ脆弱性情報をカテゴリ化・報告するためのデータベースです。
- バグにはロジックの欠陥や、メモリエンスイティを引き起こす深刻な脆弱性の両方があります。
- オンライン上で「Rust はメモリ安全であるため、CVE 報告は不要」という主張が存在します。
- 特に C/C++ の開発者は、この前提に基づいた考え方を示す傾向があります。
しかし、Rust でもメモリエンスイティや未定義の動作(UB)を引き起こすことは可能です。 単に「Rust で UB が起こらない」と主張するのは誤りであり、本稿はその実態と C/C++ との違いを解説します。
Curl における潜在的不具合
libcurl は世界中で最も広く使用されているオープンソースライブラリの一つです。開発者である Daniel Stenberg 氏は長年にわたり、堅牢なソフトウェアの構築を目指して尽力しています。
テストケース:C コードでの NULL ポインタ渡送
以下の単純な C プログラム(
curl_getenv に NULL を渡す)を実行するとどうなるでしょうか?
#include <curl/curl.h> int main(void) { curl_getenv(NULL); }
このコードはコンパイル時に警告を発生せず、実行時には セグメンテーション・フォールト が起きます。これはメモリエンスイティのバグです。
$ gcc test.c -otest -lcurl -Wall -Wextra $ ./test Segmentation fault (core dumped)
C での CVE 報告の是非
- 結論: これは
への CVE として報告すべき**「誤った使用方法」**であり、ライブラリ自体の不具合ではありません。libcurl - 理由 1(API の契約が明確ではない): C では API の前提条件(例:NULL を渡さないこと)をドキュメントに明記するのが困難です。「NULL は禁止されている」という警告がない場合でも、それは「作者の意図した正しい使い方の外」です。
- 理由 2(爆発的な CVE 数): C/C++ では、あらゆる関数で UB を引き起こす呼び出し方を見つけやすく、「すべての誤った使い方を報告」すると数百万件の CVE が発生し、データベースが無意味になります。
したがって、C/C++ ではライブラリの誤った使用方法は通常、CVE と見なされません。
Rust ではどう違うのか
Rust におけるアプローチは C/C++ と決定的に異なります。ここでは
hyper(C 版の curl に概念的に近い Rust のネットワークライブラリ)を例に挙げます。
テストケース:Rust コード
以下の Rust プログラムは、メモリエンスイティの問題を引き起こすシナリオ(例:有効な型ではない引数を渡すなど)を想定しています。
fn main() { hyper::foo(None); // 注: コンテキストによっては `None` が安全でない引数となる場合あり }
コンパイル時にエラーが出ず、実行時にメモリエンスイティ問題が発生した場合、これは Rust ライブラリ (
) への CVE として報告されます。hyper
Rust と C/C++ の決定的な違い
| 特徴 | C/C++ | Rust |
|---|---|---|
| UB の原因の归属 | アプリケーションコード側の責任(誤った使用方法)とされることが多い | ユーザーコードに を使っていなければ、ライブラリの健全性(Soundness)の欠陥とされる |
| API の安全性保証 | 明示的ではないことが多い | 明示的な型システムによって保証される |
キーワード | 頻繁に使われる必要があり、どこが危険か常に不明確な場合が多い | メモリエンスイティリスクを秘めたコードのみを囲む必要がある。ユーザー側で を使っていない限り、安全であることが保証される |
なぜ Rust の CVE が「より厳格」なのか
- 健全性(Soundness)の概念: Rust において、
ブロックを使っていない限り、メモリエンスイティを引き起こすことは不可能です。もしそのようなバグが発生すれば、それは**ライブラリの API が不健全(unsound)**であることを意味します。unsafe - 自動的な保護: ユーザーコードで
を使用していない場合、コンパイラエラーが出るか、あるいは安全に実行されます。ライブラリ側が安全なインターフェースを公開しているため、ユーザーは自動的にメモリエンスイティから保護されています。unsafe - 回答の簡素化:
ではない関数 → 「YES」(正しい使用法でメモリバグを起こせない)unsafe
の関数 → コード内をunsafe
で囲む必要がある(明示的な危険性の宣言)。unsafe ブロック
結論
- Rust と C/C++ では CVE の定義基準が異なる: コード量あたりの CVE 数を単純比較することは誤解を招きます。C/C++ は「持ち方を間違える」だけで脆弱性が生じる仕組みであり、Rust は「API 自体が不健全なら CVE」という明確なラインがあります。
- 例示の普遍性:
を例に挙げましたが、この違いはほぼすべての C/C++ ライブラリおよび他のメモリエンスイティ言語全般にも当てはまります。libcurl - 理解の深化: 「Rust でメモリエンスイティバグを起こせない」という認識は、**「
を使用していない限り、API の健全性が保証されている」**という事実に基づいていまするべきです。unsafe
もし異なる視点を持っていてディスカッションしたい場合は、コミュニティでお知らせください。