Home / CRT のセキュリティ強化版

CRT のセキュリティ強化版

2017-2-25 sscanf_s
2015-7-9 雑記(/GL)追加。
2015-4-29 /GS について追記。
2011-4-23 少し整理。
2011-2-19 追記。

今までユニコード ビルドはスルーして来たが、セキュリティ強化版 CRT で混乱したのでメモ。

VS2005 以降、次のようなコードをビルドすると…

TCHAR sz[32];
_stprintf(sz, _T("%s"), _T("abcdef"));

次のようなメッセージを食らう。

warning C4996: '_swprintf' が古い形式として宣言されました。
...\include\stdio.h(506) : '_swprintf' の宣言を確認してください。
メッセージ: 'This function or variable may be unsafe. Consider using _swprintf_s instead. To disable deprecation, use _CRT_SECURE_NO_DEPRECATE. See online help for details.'

これを黙らせたいなら _CRT_SECURE_NO_DEPRECATE を定義するか warning プラグマを使え、と書いてある(ウゼー奴だ…)。 んじゃ _CRT_SECURE_NO_DEPRECATE を定義してみよう...

#define _CRT_SECURE_NO_DEPRECATE

ビルド! ... ( ゚Д゚)ハァ? 警告止まらないじゃん。早々にまたバグかよ、マイクロ。と、思っていると ... メッセージの内容が違うんですよね、今度は。

メッセージ: 'swprintf has been changed to conform with the ISO C standard, adding an extra character count parameter. To use traditional Microsoft swprintf, set _CRT_NON_CONFORMING_SWPRINTFS.'

日本語マニュアルにはこう書かれている...
Visual C++ 2005 では、swprintf 関数は ISO C 規格に準拠しています。
この規格では、2 番目のパラメータ count を size_t 型で指定する必要があります。
古い非標準の動作を強制的に実行させるには、_CRT_NON_CONFORMING_SWPRINTFS を定義します。
この古い動作は、将来的には削除される可能性があるので、規格に準拠した新しい動作を使用するようにコードを変更する必要があります。

int swprintf(wchar_t *buffer, 
             size_t count,
             const wchar_t *format [,argument]...);

う~ん、パラメータを増やしたのは判った、じゃ、警告を出しつつもビルドが成功するのは何故か?
 詳しいカラクリは判らないが swprintf.c に count パラメータ無しのものが存在するので、そちらが使われるのだろう。その関数の書式はマニュアルに書かれていないのでお得意の隠し関数のつもりか…。

では_CRT_NON_CONFORMING_SWPRINTFS を定義してみよう。

#define _CRT_SECURE_NO_DEPRECATE
#define _CRT_NON_CONFORMING_SWPRINTFS

確かに警告は出なくなる。でも、_stprintf を使いつつ count パラメータを与えるにはどうするの?
--> おそらく汎用テキスト ルーチンのマップは諦めて swprintf と書かねばならないんでしょう。

#define _CRT_SECURE_NO_DEPRECATE
…
TCHAR sz[32];
swprintf(sz, 32, L"%s", L"abcdef");

う~む、ここまで変更する気が起こらない。

セキュリティ保護されたテンプレート オーバーロード

マニュアルの説明が酷い。グダグダ書かれていて判らない。
そもそも最初に定数(識別子)を示さないからだ。

#1. _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES ......... デフォは無効 (0)
#2. _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT ... デフォは無効 (0)
#3. _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES ........... デフォで有効 (1)

これらがどんな働きをするか示せばいい。

#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES         1
... 
char szBuf[10]; 
strcpy(szBuf, "test"); // ==> strcpy_s(szBuf, 10, "test")

strcpy の呼び出しが strcpy_s の呼び出しに変換され、サイズ引数も自動的に指定される。
なお、strncpy などのカウントを取る関数が書かれているコードには影響しない。


#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES         1
#define _CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES_COUNT   1
... 
char szBuf[10] = {0, };
strncpy(szBuf, "test", 4); // ==> strncpy_s(szBuf, 10, "test", 4)

カウントを取る関数にテンプレート オーバーロードが有効になる。とあるが、例が示されていない。
_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES も共に有効にしないと効果が無いようだ。
上の例は、推測。
なお、strncpy_s の最後の引数に _TRUNCATE を与えると strlcpy のような動作になる。ややこしい…


#define _CRT_SECURE_CPP_OVERLOAD_SECURE_NAMES           1
... 
char szBuf[10]; 
strcpy_s(szBuf, "test"); // ==> strcpy_s(szBuf, 10, "test") 

セキュリティ強化版の関数のテンプレート オーバーロードが有効になる。
一見、はぁ?と思うが、既存のコードで「サイズ引数の追加は面倒だろうから関数名だけ変えてくれれば後は何とかする。」だろうか…

その他、諸々

C4996 の原因には他にも色々ある。
warning C4996: 'getch' が古い形式として宣言されました。

ヘルプでは、Visual C++ 2005 では、この POSIX 関数は使用しないでください。代わりに ISO C++ 準拠の _getch を使用してください。とある。
--> これはその通りに変更すれば出なくなる。kbhit も同様。

しかし、今後、古いもの全てを C4996 で警告されたら面倒に思う。世の中、Microsoft が思っている程にはバージョンアップしない。


直接関係無いが、紛らわしい次のメッセージが出る場合がある。
warning C4819: ファイルは、現在のコード ページ (932) で表示できない文字を含んでいます。データの損失を防ぐために、ファイルを Unicode 形式で保存してください。

これは Microsoft がバグを認めていたと思う。

#pragma warning(disable: 4819)

sscanf_s

軽い気持ちで初めて使ってみた処、動作せず嵌った。
何故動作しなかったか? 気持ちが軽かったから、とは強ち冗談でもなく、定義はこう↓なっていて sscanf に同じに見える。

int sscanf_s(const char *buffer, const char *format [,argument ] ...);

これが落とし穴で、コメント(MSDN の)を良く読むと、

sscanf とは異なり、型フィールド文字 c、C、s、S、または [] で囲まれた文字列のコントロール セットを使用する場合にバッファー サイズのパラメーターが必要です。

だと。
wchar 版を例にこう書いてある。
wchar_t ws[10];
swscanf_s(in_str, L"%9s", ws, (unsigned)_countof(ws));

バッファサイズを渡すように書かなかったのが原因。
でも(そこまで変えるんなら %9s が妥当かどうか検査してる訳?)と言いたくなる。
新規は兎も角、既存コードを sscanf_s へ修正する作業は対価に見合う気がしない。

そもそも _s系のドキュメント(欧米製ドキュメント全般)って、何をどう変えたか、何故ド頭に書かないのかね? 効率悪し…

/GS バッファーのセキュリティ チェック

CRT じゃなく VC++ コンパイラの話し。
MSDNオンライン

/GL (プログラム全体の最適化)

とあるプロジェクトファイルを強引に古い VS2003 でビルドしようとすると、
xxxxx コマンド ライン error D2016 :コマンド ライン オプション '/GL' と '/YXstdafx.h' は同時に指定できません
となってしまった。間違ってオプション弄ってしまったか?

--> 何度か起き、そうそう弄る訳ないので .vcproj ファイルの Version= 属性を書き換える、強引な方法が問題と思う。仕方ない、自己責任だ。

なお、画面上は「全般」に設定が有り、はい/いいえ である。/GL とは表示してくれないので暫し探してしまった。

規定では /GL は無効らしいのでそうする。が、マニュアル説明無いので不審に思ふ…
MSDN に リンク時に、.obj ファイルのプリコンパイル済みヘッダー ファイルの情報が必要になる。 とあるので依存度が強いのだろう。活かすには面倒そうだ。


Home / CRT のセキュリティ強化版

© 2008 usskim    http://usskim.web.fc2.com/
inserted by FC2 system