6月20日、Phoronixが「Linux Finally Eliminates The strncpy API After Six Years Of Work, 360+ Patches」と題した記事を公開した。Linuxカーネルの次期バージョンLinux 7.2のマージウィンドウにおいて、長年にわたりバグの温床とされてきたstrncpy関数がついに完全削除された。その達成に要したのは約6年間・362コミットだ。
なぜ「1つの関数を削除する」だけにこれほどの時間がかかったのか。答えはカーネルの広大さと、strncpyの挙動の「微妙な複雑さ」にある。機械的な置換では済まない判断が何百箇所にも及んだ。その経緯と意義を紹介する。
strncpyとは何が問題だったのか
strncpyはC標準ライブラリに含まれる文字列コピー関数で、UNIX黎明期から存在する古参APIだ。しかしその挙動は長年「罠」として知られてきた。問題点は主に2つある。
NUL終端の非対称な挙動:コピー元が指定バイト数に満たない場合、残り領域をNULバイトで埋めるが、コピー元が指定バイト数以上の場合はNUL終端を付与しない。この「長い文字列をコピーするとNUL終端されない」という動作が、バッファオーバーリードやメモリ破壊の原因として繰り返し問題を引き起こしてきた。
不要なゼロ埋めによるオーバーヘッド:コピー先バッファの残り全領域を常にゼロ埋めする仕様になっており、多くのケースで不要な処理コストが生じていた。
Linuxカーネルの開発者たちはこの関数を公式に「persistent source of bugs(バグの持続的な発生源)」と位置づけ、段階的な排除を進めてきた。
誰が、いつから始めたのか
この削除プロジェクトを長年主導してきたのは、Googleのセキュリティエンジニアで、Linuxカーネルのセキュリティ強化に多数貢献してきたKees Cook氏だ。strncpyの問題が広く認識されるようになった2018〜2019年ごろから置き換え作業が本格化し、以降6年にわたって継続的にパッチが送り込まれてきた。
代替として推奨されてきたのがstrscpy()だ。カーネル独自のAPIとして整備されたこの関数は、コピー先が常にNUL終端されること、コピーしたバイト数(バッファ不足時は負値)を戻り値で返すためエラー検出が容易な点が特徴で、strncpyの問題点を設計段階から解消している。
最終マージで全アーキテクチャの実装を一掃
今回のマージコミットでは、strncpy APIそのものの削除に加え、x86やARMなど各CPUアーキテクチャごとに個別実装されていたstrncpyも一括で除去された。アーキテクチャ固有の実装まで全て片付けて初めて「完全削除」が成立した形だ。
用途別の代替API
カーネルコード内でstrncpyを使っていた箇所は、呼び出し箇所の意図に応じて以下の関数に置き換えられた。
| 旧API | 新API | 用途 |
|---|---|---|
strncpy |
strscpy() |
NUL終端が必要な通常の文字列コピー |
strncpy |
strscpy_pad() |
NUL終端+残領域のゼロ埋めが必要な場合 |
strncpy |
strtomem_pad() |
NUL終端が不要な固定長フィールドへのコピー |
strncpy |
memcpy_and_pad() |
明示的なパディング付きの境界コピー |
strncpy |
memcpy() |
コピー長が既知のメモリコピー |
この置き換えが単純な機械的作業にならなかった理由がここにある。各呼び出し箇所について「NUL終端が必要か」「ゼロ埋めが必要か」「コピー長は既知か」を個別に判断した上で適切な関数を選ばなければならなかった。ドライバ、ファイルシステム、ネットワークスタックなどカーネルの広範な領域に分散した呼び出しを一件ずつ精査した結果が、362コミット・6年という規模だ。
「負の遺産」撤去の難しさ
Linuxカーネルは2600万行超のコードベースを持ち、そこには数十年分の歴史的なAPIが積み重なっている。strncpyの削除は、その「負の遺産」を撤去することがいかに根気のいる作業かを示す典型例だ。コードの正確さより後方互換性が優先されがちな巨大プロジェクトにおいて、6年越しでゴールにたどり着いたこと自体が一つの成果といえる。
詳細はLinux Finally Eliminates The strncpy API After Six Years Of Work, 360+ Patchesを参照していただきたい。