Article

C++26標準ライブラリのhardening――範囲外アクセスを止める仕組みと設定

C++26の標準ライブラリhardeningは、選ばれた事前条件の違反を検出する仕組みです。範囲外アクセス時の停止とat()の例外処理の違い、主要ライブラリの設定方法から、既存コードへの取り入れ方を整理します。

Share

こはるの読みどころ

チェックを有効にした後、失敗した処理がどうなるかまで見てみよう。入力を受け付ける場所と、バグを検出して止める場所を分けると理解しやすいよ。

こはるの読みどころ

C++のコンテナを使うとき、添字が範囲内にあることは呼び出し側が守る約束です。その約束が崩れた瞬間を、ライブラリ側でも検出できたらどうでしょうか。

2026年10月30日、ISO C++ Blogが標準ライブラリのhardeningを扱うBartlomiej Filipek氏の記事を紹介しました。C++26のこの仕組みを理解するには、「チェックが入る」ことに加えて、チェックに失敗した後の動作まで見る必要があります。

既存のC++コードに取り入れるなら、例外で回復する処理との違いと、実際に使う標準ライブラリの設定が判断の軸になります。まずは、身近なvectorの添字アクセスから考えてみましょう。

vectorの範囲外アクセスは、hardeningで例外になるのか

要素数が3のstd::vector<int>では、有効な添字は0、1、2です。v[3]は範囲外になります。一方、同じ添字をv.at(3)へ渡すとstd::out_of_rangeが送出されます。この境界条件は、標準草案のシーケンスコンテナ要件で確認できます。

C++26のhardeningが加えるのは、一部の事前条件を実行時に検査する規定です。現在の標準草案のhardened preconditionsでは、hardened implementationは対象条件を関数のほかの観測可能な副作用より前に検査し、違反時には実行を継続しない評価方式を使います。非hardened実装で同じ条件を破れば、未定義動作です。

操作と設定 範囲外の添字を渡した場合
v.at(i) std::out_of_rangeを送出する
v[i]、対象のhardeningが有効 契約違反として検出し、終了する
v[i]、非hardened実装 未定義動作になる

つまり、hardening付きのoperator[]をtry/catchで囲んでも、at()と同じ回復経路にはなりません。利用者が範囲外の番号を入力し得るなら、アクセス前に範囲を検証するか、例外を扱う設計を選びます。入力として起こり得る失敗と、プログラムの約束違反は、扱う場所を分けたいですね。

optionalの空状態から分かる、検査の対象と限界

この仕組みは添字だけが対象ではありません。例えばstd::optionalのoperator*とoperator->には、「値を保持している」というhardened preconditionがあります。空の状態を例外として扱うvalue()とは役割が異なり、こちらはstd::bad_optional_accessを送出します。optionalのアクセス仕様

ユーザー名が未設定なら代替表示を出す、といった処理では、空であること自体は正常な状態です。has_value()による分岐やvalue_or()で、その状態をアプリの処理として扱えます。hardeningは、値があるつもりで無条件に参照してしまった箇所を見つけるために役立ちます。

背景には、既存コードの呼び出し方を大きく変えずに、本番でも使える検査を加える考え方があります。2025年2月14日付の提案P3471R4は、低い実行時負荷を重視し、すべての事前条件を検査することは目標にしていません。従来のベンダー独自の検査に共通の規定を与える、という位置付けです。

したがって、hardeningの有効化を、プログラム全体のメモリ安全性の証明として扱うことはできません。対象となるライブラリ操作を守る層として捉え、入力検証やほかの不具合検出手段と組み合わせるのが適切です。

有効化はC++の言語モードより、標準ライブラリの設定を見る

規定を理解したら、次はビルド設定です。標準草案は、実装がhardened implementationであるかを実装定義としています。C++26の言語モードを選んだという情報だけでは、チェックの有効・無効は決まりません。

主なライブラリには、次の設定手段があります。これは各実装が提供する検査の入口であり、C++26への完全対応を保証する一覧ではありません。

標準ライブラリ 明示的な設定例
libstdc++ -D_GLIBCXX_ASSERTIONS
libc++ -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST
MSVC STL /D_MSVC_STL_HARDENING=1

libstdc++のマクロ文書では、_GLIBCXX_ASSERTIONSが事前条件の追加検査を有効にします。現行文書は最適化なしでは既定で有効、最適化ありでは既定で無効と説明しています。古い版まで同じとは限らないので、開発ビルドで止まったことだけでリリースビルドも検査済みと判断しないようにします。

libc++のhardening文書は、多くのプロジェクトにFASTを推奨し、DEBUGは本番以外向けとしています。ユーザー側のマクロ指定は、ベンダーがビルド済みのライブラリ部分の設定を変更しません。既定値も配布元に依存します。

MSVC STLの公式Wikiでは、全体設定に加えて型ごとの設定も用意されています。_ITERATOR_DEBUG_LEVELには設定間のABI互換性という別の論点があるため、hardeningと同じスイッチとして置き換えないことが大切です。

本番のビルド条件で、検出後の動作まで確かめる

導入時は、コンパイラ名に加えて、標準ライブラリの種類・版・設定を記録するところから始められます。例えばlibc++のC++26対応表は言語機能の対応表と区別されており、ライブラリ側の実装状況を追うための資料です。

検証では、本番と同じ最適化条件で正常な処理を動かし、さらに隔離したテスト用プロセスで範囲外アクセスや空のoptional参照を試すと、設定の効き方を切り分けやすくなります。ここで示すのは検証方針であり、特定環境での実測結果ではありません。

終了する動作を選ぶ以上、サービスでは異常終了をどう記録し、どの単位で復旧するかも導入判断に含まれます。検査の負荷は自分たちのワークロードで測り、検出された違反は呼び出し側の前提を見直す材料にします。

hardeningを取り入れる意味は、既存コードの約束違反を、見つけて止められる箇所を増やすことです。回復可能な入力エラーはアプリで扱い、見逃したプログラムの誤りにはライブラリの検査を重ねる。その役割分担が、設定を有効にする一歩を実務につなげてくれます。

出典

Share

Related Articles

カテゴリやタグが近い記事を続けて読めるように並べています。