「Rustの方がメモリ安全」という主張はなぜ成り立つのか

Rust と C/C++ のセキュリティを比較する際、「CVE の数」を単純に数え上げる議論が散見されます。「Rust にも脆弱性は存在するではないか」という質問も、プログラミング言語の選定時によく耳にします。

しかし、この比較方法は本質的な問題を見落としています。脆弱性の性質そのものが言語によって異なるということです。

メモリ安全性の問題は、Rust では言語仕様によって構造的に制限されていますが、C/C++ では実行時まで問題が顕在化しないという違いがあるのです。

この違いを理解するには、まず Rust のメモリ安全性のメカニズムを押さえる必要があります。Rust は所有権システムによって、コンパイル時にメモリ安全に関する多くの問題を検出します。一方、C/C++ では、そうした検査は行われないため、問題は実行時になって初めて明らかになります。

curl の事例から見える「顕在化するかどうか」の分岐点

libcurl は世界で最も広く使われている開発者向けネットワークライブラリの一つです。30年近く保守され続け、多くの優秀な開発者が関わっています。その curl ライブラリの curl_getenv という単純な関数について、興味深い例が示されています。(参考)

以下の 5 行の C プログラムを見てください。

#include <curl/curl.h>
int main(void) {
    curl_getenv(NULL);
}

このプログラムはコンパイル时にはいかなる警告も出ません。しかし実行すると、セグメンテーション障害(segfault)が発生し、メモリ安全性の問題が顕在化します。これは形式的には脆弱性と言えます。

重要なのは、このような問題が「大規模なプログラムでは、偶然にいくらでも起こりうる」という点です。C/C++ では、NULL ポインタ逆参照は言語仕様では許可されており、エラーとして検出される保証がありません。

Rust では「unsafe」キーワードが果たす役割

では、同じ状況を Rust で書いた場合はどうでしょう。

Rust でメモリ安全に関連する脆弱性を発生させるには、ほとんどの場合 unsafe キーワードを使う必要があります。これは意図的に安全検査を無効化することを、コード上で明示的に宣言するということです。

つまり、Rust において CVE に該当するメモリ安全性の問題は、以下のような特徴を持ちます。

  • コード上に unsafe ブロックが存在することで、その部分が危険であることが一目瞭然
  • 開発者や保守者が意識的にセキュリティレビューの対象にする傾向が高い
  • ライブラリ化する場合、unsafe なコード領域は通常、限定的にカプセル化される

つまり Rust では「メモリ安全性に関連する潜在的な問題」が、言語レベルで可視化・制限される構造になっているわけです。

一方、C/C++ では、メモリ管理の問題はいたるところに潜在している可能性があり、大規模なプログラムの中から脆弱性を見つけることは格段に難しくなります。

CVE 数だけで言語を評価する危険性

この視点から改めて考えると、「Rust には CVE が存在するか」という質問に対して「だから Rust も安全ではない」と結論づけるのは、いくぶん一面的だと言えます。

考慮すべき点は以下の通りです。

  • C/C++ の問題:潜在的なメモリ安全性の問題が多く存在していても、報告されない・発見されない可能性が高い
  • Rust の問題:unsafe ブロックに関連する問題は、相対的に発見・報告されやすく、CVE データベースに登録される傾向がある
  • 可視性の違い:言語レベルで問題が構造化・制限されていることで、セキュリティレビューがより徹底しやすい

また、Rust では一般的なロジックエラー(例:管理者ダッシュボードへのアクセス制御漏れ)も、当然ながら発生する可能性があります。しかし、メモリ安全性に限定すれば、言語仕様による防御の有無は明確です。

項目 C/C++ Rust
メモリ安全性の責任 開発者の判断 言語仕様で制限
潜在的な問題の可視性 低い(実行時まで不明確) 高い(コンパイル時&unsafe で明示)
セキュリティレビューの対象化 全体 unsafe ブロックに集中
脆弱性発見の難易度 高い(大規模コード内での発見は困難) 相対的に低い(問題が局所化)

実務的な視点—セキュリティ判断に必要な情報

セキュリティを重視するエンジニアの方々にとって、ここから得られる実用的な示唆は何でしょうか。

まず、CVE 数の単純比較だけで言語選定を判断するのではなく、「メモリ安全性の問題がどのように制御されているか」という構造的な違いを理解する必要があります。特に、クリティカルなシステムやネットワーク周辺の処理を扱う場合、Rust の所有権システムが提供する保証は、運用コストの削減にも直結する可能性があります。

同時に、Rust にも脆弱性は存在し得ます。unsafe ブロックやライブラリの依存管理には、やはり人間による細心の注意が必要です。ただし、その「注意が必要な範囲」が言語レベルで明確化されているというのが、C/C++ との本質的な違いなのです。

このような観点からメモリ安全言語の導入を検討する際には、単なる脆弱性の数を比較するのではなく、「問題がどこに潜在し、どのように制御されているか」を深掘りすることが重要です。