2025/12/04 5:37
Checked-size array parameters in C
RSS: https://news.ycombinator.com/rss
要約▶
LWN.netが紹介する記事は、Linuxカーネルで配列サイズをチェックできるC言語の「static」キーワード使用法について説明しています。
主旨:
- 配列パラメータに
を書けばコンパイラがサイズ不足を検出し、引数順序ミスを防げる。static N - 既存の配列ポインタトリックより簡潔で呼び出し側コード変更不要。
- Torvaldsも支持し、カーネル内で実装例が増えている。
重要ポイント(3つ)
を使ったプロトタイプはサイズチェックを自動化する。static- カーネルでは警告抑制のために追加対応が必要だが、Clang等では有効。
- この記事で「静的配列パラメータ」が安全性向上として採用されつつあることを示す。
本文
LWN.netへようこそ
以下の購読者限定コンテンツは、LWN のサブスクライバーによってあなたに提供されています。何千ものサブスクライバーが、Linux とフリーソフトウェアコミュニティから最高のニュースを得るために LWN に依存しています。この記事がお役に立った場合は、ぜひ LWN を購読してください。LWN.net へのご訪問ありがとうございます!
C 言語で指定された最小限のチェックでは検出できない多くのプログラマミスがあります。そのうちの一つが、関数へ渡す配列サイズを誤ることです。暗号レイヤー内で配列パラメータに安全性を追加しようとした最近の試みでは、いくつかの巧妙なトリックが使われましたが、結局それらは不要でした。このチェックを行うための、C のあまり知られていない機能が存在し、すでにカーネル内のいくつかの場所で使用されています。
議論は、Ard Biesheuvel が詩的に名前付けされた関数
xchacha20poly1305_encrypt() の安全性を向上させようとしたときに始まりました:
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len, const u8 *ad, const size_t ad_len, const u8 nonce[XCHACHA20POLY1305_NONCE_SIZE], const u8 key[CHACHA20POLY1305_KEY_SIZE]);
この関数の潜在的な問題は、複数の
u8 配列へのポインタを受け取る点です。Biesheuvel は、nonce と key の配列サイズがコンパイラによってチェックされないことを指摘しました(ただし関数プロトタイプでは明示的に指定されています)。そのため、引数を誤った順序で渡してしまうと脆弱性につながる可能性があります。
Biesheuvel は次のようにプロトタイプを書き換えることを提案しました(太字で違いを示しています):
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len, const u8 *ad, const size_t ad_len, const u8 (*nonce)[XCHACHA20POLY1305_NONCE_SIZE], const u8 (*key)[CHACHA20POLY1305_KEY_SIZE]);
最後の二つの引数の型が変わり、配列へのポインタではなく「指定サイズの配列へのポインタ」を受け取るようになっています。呼び出し側は追加の
& 演算子を付けて正しいポインタ型にする必要がありますが、渡すアドレス自体は変わりません。この方法ならコンパイラが配列サイズをチェックでき、引数の順序ミスを検出します。
Jason Donenfeld はこのアイデアに興味を示しましたが、問題を解決するもっと直接的な方法があると考えていました。実際、C 標準の奥深くには
static キーワードを奇妙に使うことで、次のようにプロトタイプを書けるという仕組みがあります:
void xchacha20poly1305_encrypt(u8 *dst, const u8 *src, const size_t src_len, const u8 *ad, const size_t ad_len, const u8 nonce[static XCHACHA20POLY1305_NONCE_SIZE], const u8 key[static CHACHA20POLY1305_KEY_SIZE]);
これによりコンパイラは配列サイズをチェックし、呼び出し側のコードを変更する必要がありません。ポインタトリックと違い、
static を使うと渡された配列が小さすぎる場合のみ警告が生成されます。そのためすべてのミスを捕捉できるわけではありませんが、メモリ安全性や引数の入れ替え問題を防ぐには十分です。
Eric Biggers は GCC が
static を使わなくても「配列が小さすぎる」警告を生成することが多いと指摘しましたが、カーネルは現在これらの警告を無効化しているため、static を使っても抑制されてしまいます(6.8 で誤検知のために無効化)。しかし彼は Clang で警告を有効にするために static の追加が価値あると考えており、「この比較的珍しい C の機能」を Linus Torvalds が受け入れるかどうかを尋ねました。
実際、Torvalds はこの使用法に反対はしていません。彼はその設計自体よりも「static」自体の構文がひどいハックであると述べています:
「static」の問題点は、単に構文が酷いハックで、人々が全く意味を成さない既存のキーワードを選んだというだけです。さらに後方互換性の問題もないことが保証されています。
彼はカーネル内ですでにこの機能を使用している場所が多数あると指摘し、簡単な例として仮想端末ドライバの
getconsxy() を挙げました。彼は使い方をより明示的にするために min_array_size() のようなマクロで隠すことも提案しましたが、それが必須だとは思っていませんでした。Donenfeld は数日後にその方向のパッチを提出しましたが、次第に「at_least」マーカーへと転換しました。
カーネルは C をできる限り安全に使うために多くの工夫を凝らしていますが、それでもまだ取り残された低垂の果実があります。
static のような正式配列パラメータで使用する機能は安全性を向上させますが、一般には知られておらずあまり使われていません。この特定の場合では、「ひどいハック」が近い将来より広く採用されるのも不思議ではありません。
この記事の索引項目
- KernelC 言語安全性
- KernelReleases/6.19