VisualStudio2008 SP1 vectorのバグ?
以下のプログラムはstd::allocatorを継承して自作アロケータを作成し、実際よりも32byte多めに確保してその前後に
固定値を入れ、解放時に固定地を書き込んだ領域に不正書き込みがないか確認するものである。
#include <stdio.h> #include <map> #include <vector> #include <string> struct Data { std::string file; int line; size_t size; }; static std::map<void*, Data> data_; /*! * @brief アロケータクラスを提供します。 */ template <class T> class CAllocator : public std::allocator<T> { public: typedef typename std::allocator<T>::size_type size_type; typedef typename std::allocator<T>::pointer pointer; template<typename U> struct rebind { typedef CAllocator<U> other; }; CAllocator() {} CAllocator(const CAllocator<T> &) {} template <class U> CAllocator(const CAllocator<U> &) {} // メモリを割り当てる pointer allocate(size_type size, const void* hint = 0) { Data data; char* p = ::new char[size + 32]; memset(p, 0xFD, 16); memset(p + size + 16, 0xFE, 16); data.size = size; p += 16; data_[p] = data; printf("alloc 0x%p size:%u\n", p, size); return reinterpret_cast<pointer>(p); } // メモリを解放する void deallocate(pointer p, size_type num) { printf("deallocate 0x%p\n", p); if (data_.count(p) != 0) { Data data = data_[p]; char* pp = reinterpret_cast<char*>(p) - 16; size_t end_off = data.size + 16; for (int i = 0; i < 16; ++i) { unsigned char c1 = pp[i]; unsigned char c2 = pp[end_off + i]; if (c1 != 0xFD) { // 先頭16バイトが不正アクセスされた // printf("0x%02x %pの前方に不正アクセス FILE:%s LINE:%d\n", c1, p, data.file.c_str(), data.line); printf("0x%02x %pの前方に不正アクセス\n", c1, p); } if (c2 != 0xFE) { // 先頭16バイトが不正アクセスされた // printf("0x%02x %pの後方に不正アクセス FILE:%s LINE:%d\n", c2, p, data.file.c_str(), data.line); printf("0x%02x %pの後方に不正アクセス\n", c2, p); } } ::delete pp; data_.erase(pp); } else { // 登録されていない不正なポインタ } } // 割当てることができる最大の要素数を返す size_type max_size() const { return 100 * 1024 * 1024; } }; int main() { std::vector<char, CAllocator<char> > buf(100); }
このプログラムを起動した場合、期待する出力というのは以下となる
alloc 0x00678EF0 size:100 deallocate 0x00678EF0
アドレスは環境によってことなるが、期待する動作はこれである。VS2008で起動した場合はこの結果が得られた…Debugビルドではな!
Releaseビルドして実行すると驚くべき結果になった
alloc 0x00098F60 size:1 alloc 0x00091730 size:100 deallocate 0x00091730 deallocate 0x00098F60 0xfb 00098F60の後方に不正アクセス 0x1d 00098F60の後方に不正アクセス 0x00 00098F60の後方に不正アクセス
これはいったいどういうことだ!
size1の領域取得、さっぱりわからん。いったい何のために取得しているんだ。
取得と解放のログを見る限り、1byteの確保と100byteの確保は別の目的で使用されているらしい。
解放ログが100byte解放の後に1byte解放しているからね。
しかし、その後不正アクセスが入っている。グローバルnewで確保している以上、確保領域が被ることはない
つまり、何かしらの理由で不正アクセスがされているのは確定である。誰か説明して><