/note/tech

C言語は「危険」と言われても、専門家が「使う価値は十分にある」と言うワケ

要約:

■ 1. C言語の現状と安全性の課題

  • C言語の利用範囲:
    • Linuxカーネルをはじめ、OSや組み込み機器など幅広い分野で使われている
    • 高速なプログラムを作りやすく、移植性にも優れる
    • 半世紀以上たった今も、簡単に引退できる言語ではない
  • 安全性を巡る課題:
    • メモリ管理をプログラマーに委ねる部分が多い
    • 境界外アクセスや解放済みメモリの使用などが問題として指摘されている
    • 規格上の動作が定められていない未定義動作も課題の一つ
  • 「危険な言語」という評価:
    • ハードウェアを直接制御できる一方、現代の言語が備える安全装置がほとんどない
    • それでもC言語の安全性の改善は進んでいる
  • 安全な言語への乗り換え論:
    • 問題が多いなら安全な言語に乗り換えればよいという考え方がある
    • しかし標準化に関わる専門家は、2026年でもC言語を使う価値は十分にあると主張する

■ 2. ユーカー氏の講演とC言語を使う価値

  • 講演の出典:
    • LWN.netが2026年9月28日に「Reducing undefined behavior in the C language」を公開
    • パリの技術カンファレンス「Kernel Recipes」でのマルティン・ユーカー氏の講演の報告
  • ユーカー氏の経歴:
    • 生体医工学の教授
    • MRI装置を制御する自由ソフトウェアに取り組んでいる
    • C言語の標準化委員会「WG14」に関わっている
  • C言語を使う価値:
    • 移植性と長期的な安定性
    • コンパイルの速さと、生成されるプログラムの速さ
    • ソースコードからコンピュータの動作を把握しやすい
    • 利用できる開発ツールが多い

■ 3. 抽象機械と未定義動作の由来

  • C89の制定背景:
    • 1989年に制定された初の標準規格
    • 当時存在したさまざまなコンピュータへの対応が求められた
  • ハードウェアの多様性:
    • 符号付き整数の表現方法が異なる機械が存在した
    • メモリを区切って扱う機械や、特殊なポインターを使う機械もあった
    • そのため移植性の高い規格を作ることは非常に難しかった
  • 抽象機械というアプローチ:
    • C言語を特定の実機ではなく抽象機械で動作する言語と位置付けた
    • 抽象機械は物理的なハードウェアを持たない、理論上の架空のコンピュータ
    • プログラムの動作と結果を定めるための共通モデルとして機能する
    • 内部処理が異なっても、外部から観測できる結果が同じならよい
  • コンパイラの自由度:
    • 結果を変えない範囲で命令の省略や順序の入れ替えによる高速化が可能
    • 規格はコンパイラ実装者に多くの自由度を与えている
  • 未定義動作とは:
    • 移植可能でない処理や、規格で動作を定めていない処理の実行時に発生する
    • C規格は未定義動作に対し処理系に「いかなる要件も課さない」と規定している
  • 未定義動作を設けた狙い:
    • 独自機能の追加やハードウェアの安全機構との連携を可能にする
    • 積極的な最適化を可能にする
    • 検出の難しい誤りを実装側が無視できるようにする

■ 4. 未定義動作の「タイムトラベル」問題

  • 未定義動作の厄介な点:
    • 問題のある命令だけが予想外の結果になるとは限らない
    • コンパイラは未定義動作に到達する状況を正常なプログラムでは起こらないと仮定する
    • その結果、未定義動作へ至る前の処理まで変更する場合がある
  • ゼロ除算の例:
    • 条件に応じて外部変数へ42または43を代入し、その後で割り算を行う関数を想定する
    • 割る数が0なら整数のゼロ除算となり未定義動作が発生する
    • コンパイラは割る数が0の場合を考慮不要と判断し、条件分岐を省略する
    • 結果として常に42を代入するコードに変えることがある
  • 関数呼び出しに置き換えた場合:
    • 呼び出した関数がプログラムを終了させるなら、後のゼロ除算は実行されない
    • この場合は未定義動作が発生しないため、条件分岐を省略できない
    • 実際にこの違いを正しく扱えなかったコンパイラもあった
  • volatile変数の場合:
    • 外部から観測できるvolatile変数への書き込み後にゼロ除算がある状況を想定する
    • コンパイラが除算を先に実行すると、先に行われるはずの書き込みまで消える可能性がある
  • 「タイムトラベル」:
    • 後の未定義動作が以前の処理へさかのぼって影響するように見える問題の呼称
  • C23による対処:
    • 2024年制定のC23で、このような命令の移動を防ぐ規定が加えられた
    • これによりタイムトラベル問題は解消される見込み
  • 残る問題:
    • 初期化されていない変数の読み出しやポインターの比較が挙げられる
    • 規格の解釈やコンパイラの実装を巡る問題が他にも残っている

■ 5. 未定義動作の削減と検出手段

  • 標準化委員会の研究グループ:
    • メモリオブジェクトモデル、メモリ安全性、未定義動作に特化した3グループを運営している
  • 未定義動作の削減:
    • 現行規格には約100個の未定義動作がある
    • 次期規格「C2y」の草案では、そのうち45個がすでに削除された
    • C言語の問題の幾つかは未定義動作に関連するため、削減により状況は改善している
  • 問題を見つける手段:
    • コンパイラの警告と静的解析ツール
    • 実行時に異常を検出するSanitizer
    • LLMを利用したコード解析
    • プログラムの正しさを数学的に確かめる形式検証
  • コンパイラ警告の拡充:
    • 整数のオーバーフローや解放済みメモリの使用も警告できる場面が増えている

■ 6. メモリ安全性の3分類

  • 型安全性:
    • 異なる種類のデータを誤って扱うことを防ぐもの
    • 注釈やコンパイラの検査の強化で改善できる
  • 空間的メモリ安全性:
    • 配列などの範囲外を読み書きしないこと
    • コンパイラによる境界検査や、配列の長さを伝える属性が役立つ
  • 時間的メモリ安全性:
    • 解放済みメモリの再使用などを防ぐもの
    • 最も解決が難しく、この点では「Rust」に利点がある
    • 一方で「CHERI」や「Fil-C」などによりCの安全性を高められる可能性がある

■ 7. 完全なメモリ安全への道筋

  • 規格変更と検査ツールの限界:
    • それだけでC言語がすぐに完全なメモリ安全言語になるわけではない
    • 全ての問題を確実に防ぐには、負担の大きい実行時検査か形式検証が必要
  • 当面の最善策:
    • 危険な機能の使用を制限したC言語と形式検証ツールを組み合わせる
    • この方法が最も高い安全性を実現しやすい

■ 8. 進化を続ける“生きた言語”

  • C23での変更:
    • 古い関数定義や過去のコンピュータ向け機能を削除した
    • オーバーフローを検査できる整数演算などを追加した
  • C2yでの変更:
    • 配列の要素数を調べる機能の追加が進む
    • 未定義動作の削減が進む
  • 将来の展望:
    • すぐに完全なメモリ安全を実現するわけではないが、将来は可能性がある
    • それを実現しやすい環境も整っていく
    • そのため標準化活動への参加を呼びかける
  • C言語の位置付け:
    • 未定義動作が45個減っても、すぐに安全な最新言語と肩を並べるわけではない
    • それでも長所を維持しながら安全性を高める取り組みが続いている
    • 完成して変化を止めた言語ではなく、問題を修正しながら進化している言語