Entries

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

疑わしきを警告するならその根拠を示せ

タグ: C++ VS2005

Visual Studio Team Editionのコード分析はそこそこ役に立つ機能ではあるが、一部APIにいい加減?な注釈が付いているため、意味の分からない警告を出すことがある。

#include <windows.h>

void hoge()
{
    if (HANDLE h = CreateFileMapping(INVALID_HANDLE_VALUE, NULL, PAGE_READWRITE, 0, 4, 0))
    {
        if (void* p = MapViewOfFile(h, FILE_MAP_WRITE|FILE_MAP_READ, 0, 0, 0))
        {
            memset(p, 0, 4);

上記コードでmemset()までたどり着くのは「4バイトのファイルマッピングオブジェクトを生成・マッピングできた」場合のみ。
それなのに、VS2005TEのコード分析は以下のような警告を出す。

(9) : warning C6386: バッファ オーバーランです: '引数 1' へアクセスしています。書き込み可能なサイズは '1*0' バイトですが、'4' バイトを書き込む可能性があります: Lines: 5, 7, 9

なぜ「書き込み可能なサイズは '1*0' バイト」と判定されているのか。
それはMapViewOfFile()の戻り値型「LPVOID」に「_out」の注釈が付いているからだ。

WINBASEAPI
__out
LPVOID
WINAPI
MapViewOfFile(
    __in HANDLE hFileMappingObject,
    __in DWORD dwDesiredAccess,
    __in DWORD dwFileOffsetHigh,
    __in DWORD dwFileOffsetLow,
    __in SIZE_T dwNumberOfBytesToMap
    );

サイズの指定のない「_out」注釈はバッファの長さが1要素分であることを示す。
戻り値型「LPVOID」の要素型は「void」なので、バッファの長さは「1*sizeof(void) = 1*0(便宜上)」バイトということになる。

void *  __cdecl memset(__out_bcount_full_opt(_Size) void * _Dst, __in int _Val, __in size_t _Size);

それに対しmemset()の第1引数_Dstは「NULL以外のポインタを渡されたら(_opt)そのポインタが指す_Sizeバイトのバッファ(_bcount)全体を初期化する(_full)出力属性の引数(_out)」とある。
この2つの注釈を合わせて解釈すると、先の9行目のmemset()呼び出しは「4バイト初期化することを明言している引数に、バッファサイズが0バイトにしかならないMapViewOfFile()の戻り値なんかを渡している」ということになるので、コード分析が警告を出したのである。

このコード分析の判断自体に問題はない。
問題なのは「MapViewOfFile()の戻り値にこの_out注釈を付けている」こと。あるいは「サイズ指定なし=_ecount(1)」という仕様。そしてなによりC6386の解説を読んでも警告されている理由が分からないことである。

スポンサーサイト

コメント

コメントの投稿

コメントの投稿
管理者にだけ表示を許可する

トラックバック

トラックバック URL
http://idlysphere.blog66.fc2.com/tb.php/231-fe1bdfe3
この記事にトラックバックする(FC2ブログユーザー)

Appendix

タグ

Blog内検索

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。