strcpy_sなんて使うな

超今さらですが、C1x?に、セキュア文字列関数群*1が正式に入るみたいですね。

http://www.open-std.org/JTC1/SC22/WG14/www/projects#24731-1

で、google:strcpy_sで検索すると、strcpyなんて使うな。安全でセキュリティの高いstrcpy_sを使え!という旨の文面がいっぱいでてくるわけですよ。馬鹿だと思いますね。この「セキュア」文字列関数群を最初に考えた人も、乗せられて使え使え言ってる人達も……。勿論strncpy*2やstrlcpyも含めて。

確かにバッファオーバーフローは阻止できますよ。スタックが上書きされて暴走したり悪意のあるコードが実行されたりするのは阻止できますよ。でも、その結果、データが欠けたまま処理が進んでしまうわけですよ。規格ではset_constraint_handler_sでオーバーフロー時のハンドラを設定できたり、VC++ではSEHに乗せてくれたりするのはマシな動作だと思います。落ちたほうがマシ。

恐らくこの背景には、従来固定長で確保した領域を使っていたレガシーコードを、簡単に、「セキュア」に書き換えることができる、というのがあるのだとは思いますが……新規コードでそれを勧めるなよ……と思います。

新規コードを書く際に恐らく正しいのは、アプリケーションコード側で領域が足りないことを検出してなんか*3するか、領域は全部動的に確保するか、ではないでしょうか。そして、このどちらの方法であっても、オーバーフローなんて起きないので、strcpy_sなんて最初から要らないわけですよ。通常のstrcpyで充分、いや長さを調べ済みですので効率も考えたらmemcpy*4が最適!memcpyに加えて次の1バイトに0を書き込んでくれる関数があったら便利ですのに。動的に領域を確保する場合(新しくコードを書く場合他に制約がなければほとんどこっちになると思います)はstrdupが超便利!strdup最強!strdupこそ至高!

で、本来、セキュア文字列関数群として実装されるべきだったのは、(strcpyは既にstrdupがありますので)strcatやsprintfの動的確保版(名前は……malloc_and_strcatなりstrcatdupなり……まあなんでもいい)だったはずです。*5 *6領域をスタックに取る版(名前は……alloca_and_strdup?*7 )もあればもっと便利でしょう。

勿論私はセキュリティの専門家でもC言語の専門家でもありませんので、実はstrcpy_sにはなんか神の魔法がかかっていて、これさえ使っておけば万事OK!なのかもしれませんが……。

あれ?なんかこれ前に書いた気がするな……?まあいいや、最近(というかもう何年もですが)Adaの標準ライブラリを再実装して遊んでまして、そろそろAda.Strings.Fixedに差し掛かってきたのですが、まあそういうことです以下略。

追記

http://twitter.com/sscrisk/status/27385893614

あれ……そうでしたっけ……(汗

http://www.cert.org/secure-coding/managedstring.html

こんなのもあるようです。
っていうか最初から↓貼ればそれだけで済んだ!!

Defensive Programming for Red Hat Enterprise Linux

どうにも私はこのへんの歴史とか経緯を全く把握していないので、頓珍漢なことを書いてしまったのかも……というか、書いたからこそ反応が貰えてそこからこれらのURLを知ることができた始末……。

追記
同じような感覚の人がいたので嬉しくなってリンク。
http://ponner.blog104.fc2.com/blog-entry-211.html

*1:secure string functions?WGですとBounds-checking interfaces。文字列を作る関数の後ろに_sが付いた奴。

*2:……strncpyがこの用途では無い事なんて重々承知しておりますただこの用途で使っているコードを見たことがあるだけです勿論私が書いたんじゃないですでも勘違いされやすい関数であることは間違いないと思いますごめんなさいごめんなさい

*3:普通は可能であればデータの復旧、その後メッセージの表示なり領域を再確保してリトライなり、abortするなりlongjmpするなり……一意な解決法は無くて、アプリケーションが工夫するところのはず。単に失敗すればいいものを失敗時デスティネーションにゼロを入れてくれやがる*_sは、失敗時にデータを元に戻すという観点からもマイナスです。

*4:memcpy_sにバッファの長さを渡すパラメータが増えているのがまた……。

*5:GMP他気の利いたライブラリは、既にそうなってますよね。mpz_get_strは長さを渡すパラメータなんてありませんのでオーバーフローチェックをする「セキュア」な関数ではありませんが、NULLを渡すと領域を確保してくれます。この方が使う側にとってもずっと便利です。

*6:っていうか探したらasprintfってありましたよ。これぐらい調べてから書け>自分

*7:これも既にstrdupaってありますね……。aが頭に付いたらmallocで後ろについたらallocaなんでしょうか。よくわかりません!