0

std::vector を unsigned char データのストレージとして使用する IBuffer という名前のクラスがあります。このクラスは、私の基盤ライブラリである core.lib の一部です。このクラスの責任は、データをバッファに書き込み、そこから読み取ることです。さて、ネットワークから受信したメッセージをシリアライズおよびデシリアライズする目的で、別のライブラリでこのクラスを使用しています。

新しいデータを std::vector にプッシュバックしようとすると、場所 0Xxxxxxx を読み取るメモリ アクセス違反に常に問題があります。ここに何千行ものコードを書くのは難しいので、スタックトレースをここに置きます。誰かがこの問題を解決するのを手伝ってくれることを願っています。

        ntdll.dll!@RtlpLowFragHeapFree@8()  + 0x2c bytes    
    ntdll.dll!_RtlFreeHeap@12()  + 0x7e bytes   
    kernel32.dll!_HeapFree@12()  + 0x14 bytes   
    msvcr100d.dll!__free_base()  + 0x29 bytes   
    msvcr100d.dll!__free_dbg_nolock()  + 0x4ae bytes    
    msvcr100d.dll!__free_dbg()  + 0x50 bytes    
    msvcr100d.dll!operator delete()  + 0xb9 bytes   
>   ICore.dll!std::allocator<unsigned char>::deallocate(unsigned char * _Ptr=0x002c7810, unsigned int __formal=1)  Line 182 + 0x9 bytes C++
    ICore.dll!std::vector<unsigned char,std::allocator<unsigned char> >::_Insert_n(std::_Vector_const_iterator<std::_Vector_val<unsigned char,std::allocator<unsigned char> > > _Where=221 'Ý', unsigned int _Count=2, const unsigned char & _Val='')  Line 1375    C++
    ICore.dll!std::vector<unsigned char,std::allocator<unsigned char> >::insert(std::_Vector_const_iterator<std::_Vector_val<unsigned char,std::allocator<unsigned char> > > _Where=221 'Ý', unsigned int _Count=1, const unsigned char & _Val='')  Line 1060   C++
    ICore.dll!Utils::IBuffer::WriteShort(unsigned short data=256)  Line 63 + 0x56 bytes C++
    ICore.dll!Serialization::IBufferWriter::writeUShort(unsigned short data=256)  Line 82   C++
    IRNet.dll!IRNetwork::IStunMessage::AcceptWriter(Serialization::IIWriter * writer=0x0040f080)  Line 340 + 0x2e bytes C++
    IRNet.dll!Serialization::IBufferWriter::WriteObject<IRNetwork::IStunMessage>(const Object::IObject * object=0x002cb5f0)  Line 105 + 0x17 bytes  C++
    IRNet.dll!IRNetwork::IStunMessage::Serialize(Object::SharedPtr<Utils::IBuffer> buffer={...})  Line 769  C++
    IRNet.dll!IRNetwork::IStun::SendUnReliably(IRNetwork::IStunMessage * message=0x002cb5f0)  Line 157  C++
    IRNet.dll!IRNetwork::IStun::SendBind()  Line 61 + 0xc bytes C++
    IRNet.dll!IRNetwork::IStun::Bind(unsigned int localfd=184)  Line 48 C++
    IRNet.dll!IRNetwork::INatContext::StunBind(unsigned int fd=184)  Line 52 + 0xc bytes    C++
    testDhcpv4.exe!TestStunAttribute::Start()  Line 48 + 0x24 bytes C++
    testDhcpv4.exe!wmain(int argc=1, wchar_t * * argv=0x002c71d8)  Line 69  C++
    testDhcpv4.exe!__tmainCRTStartup()  Line 552 + 0x19 bytes   C
    testDhcpv4.exe!wmainCRTStartup()  Line 371  C
    kernel32.dll!@BaseThreadInitThunk@12()  + 0x12 bytes    
    ntdll.dll!___RtlUserThreadStart@8()  + 0x27 bytes   
    ntdll.dll!__RtlUserThreadStart@8()  + 0x1b bytes    

IBUffer.h

#include "stdafx.h"
#include "ICoreConfig.h"
#include "ILMutex.hxx"
#include "ILock.hxx"
#include <vector>
using namespace IThreading;
using namespace std;
namespace Utils
{
/*
#ifdef EXP_STL
#    define DECLSPECIFIER __declspec(dllexport)
#    define EXPIMP_TEMPLATE
#else
#    define DECLSPECIFIER __declspec(dllimport)
#    define EXPIMP_TEMPLATE extern
#endif

    EXPIMP_TEMPLATE template class DECLSPECIFIER std::vector<unsigned char>;*/

    template class ICORE_API std::allocator<unsigned char>;
    template class ICORE_API std::vector<unsigned char,std::allocator<unsigned char> >;
    /// <summary>
    /// 
    /// </summary>
    typedef enum 
    {
        /*Begin Of Buffer*/
        BOB = 0,
        /*End Of Buffer*/
        EOB = 1,
        /*Unknown*/
        UNK = 2
    } BufferState_t;
    class ICORE_API IBuffer
    {
    public:
        IBuffer(void);
        IBuffer(IBuffer* newbuff);
        ~IBuffer(void);
        //Methods
        int Write(const void* data,size_t size);
        int Write(const void** data, size_t sz);
        int Write(size_t position , const void* data,size_t sz);
        int WriteShort(unsigned short data);
        int WriteShort(uint32_t position,unsigned short data);
        int WriteByte(unsigned char data);
        int WriteUInt(unsigned int data);
        int WriteUlong(unsigned long data);



        //************************************
        // Method:    ReadByte
        // FullName:  BufferPool::ReadByte
        // Access:    public 
        // Returns:   unsigned char
        // Qualifier:
        //Description: Read and return one byte of data by starting from begining of buffer
        //************************************
        unsigned char ReadByte();
        unsigned short ReadShort();
        unsigned int ReadUint32();
        unsigned long ReadUlong();
        unsigned char* Read(size_t sz);

        /** Converts a 16-bit value from host to TCP/IP network byte order (big-endian).
        * @param x The 16-bit (in host byte order) value to convert.
        * @retval @a x in TCP/IP network byte order.
        */
        unsigned short HostToNetworkByteOrder(unsigned short x);

        /* Memory alignment hack */
        //************************************
        // Method:    HostToNetworkByteOrder
        // FullName:  BufferPool::HostToNetworkByteOrder
        // Access:    public 
        // Returns:   unsigned short
        // Qualifier:
        // Parameter: const void * px
        // Description: This method used when we want to Pars existing buffer
        //************************************
        unsigned short HostToNetworkByteOrder(const void* px);

        unsigned short NetworkToHostByteOrder(const unsigned short x);

        /** Converts a 32-bit value from host to TCP/IP network byte order (big-endian).
        * @param x The 32-bit (in host byte order) value to convert.
        * @retval @a x in TCP/IP network byte order.
        */
        unsigned long HostToNetworkByteOrder(unsigned long x);

        /* Memory alignment hack */
        //************************************
        // Method:    HostToNetworkByteOrderUL
        // FullName:  BufferPool::HostToNetworkByteOrderUL
        // Access:    public 
        // Returns:   unsigned long
        // Qualifier:
        // Parameter: const void * px
        // Description: this method used when we want to pars existing buffer
        //************************************
        unsigned long HostToNetworkByteOrderUL(const void* px);
        //* parse unsigned int type to byte array as is
        vector<unsigned char> Uint32ToByteArray(unsigned int x);

        uint8_t* ToUInt8ptr();

        unsigned long NetworkToHostByteOrder(const unsigned long x);


/*      IBuffer operator =(const IBuffer &buf);*/
        IBuffer& operator =(IBuffer &Rhs);
        IBuffer& operator =(char* rhs);

        operator char*();
        operator void*();
        operator uint8_t*();

        //************************************
        // Method:    operator++
        // FullName:  BufferPool::operator++
        // Access:    public 
        // Returns:   BufferPool
        // Qualifier:
        // Description: This method increment dataPtr
        //              read one byte , increment dataPtr by one
        //              read short , increment dataPtr by two
        //              read unsigned int increment dataPtr by 4
        //              note : each read function set currPosion in suitable way
        //              we do this because of continuous buffer reading
        //************************************
        void operator ++();
        bool operator<(const IBuffer& buff)
        {
            return this->Size() < buff.Size() ? true : false;
        }

        bool operator==(const IBuffer& buff)
        {
            return this->buffer == buff.buffer;
        }
        //Properties
        bool IsEmpty();
        unsigned char* Value();

        /** Indicates whether we are on a Big Endian host or not.<br>
        * IMPORTANT: Before calling this function, you should initialize the network stack by using
        * @ref tnet_startup().
        * @retval @a true if the program is running on a Big Endian host and @a false otherwise.
        */
        bool IsBigEndian();
        inline unsigned char HI_Uint16(unsigned short x)
        {
            return (((x) >> 8) & 0xFF) ;
        }
        inline unsigned char LOW_Uint16(unsigned short x)
        {
            return ((x) & 0xFF);
        }
        size_t Size() const;
        void SetPtr();

        void CleanUp();

        void Move(int32_t count);
    private:
        inline int GetPosition()
        {
            return currPosition;
        }

        int SetPosition(int newpos);
    public:
        static class _Convert
        {
        public:
            static inline unsigned short ToUShort(const unsigned char* u8_ptr)
            {
                return (((unsigned short)(u8_ptr)[0]) | ((unsigned short)(u8_ptr)[1])<<8);
            }
            static inline unsigned int ToUint32(const unsigned char* u8_ptr)
            {
                return (((unsigned int)(u8_ptr)[3]) | ((unsigned int)(u8_ptr)[2])<<8 | ((unsigned int)(u8_ptr)[1])<<16 | ((unsigned int)(u8_ptr)[0])<<24);
            }

            static inline signed int ToInt32(const unsigned char* u8_ptr)
            {
                return (((signed int)(u8_ptr)[0]) | ((signed int)(u8_ptr)[1])<<8 | ((signed int)(u8_ptr)[2])<<16 | ((signed int)(u8_ptr)[3])<<24);
            }
        protected:

        private:
        } Convert;
        static BufferState_t bstate;
        Mutex mMutex;
    private:        
        vector<unsigned char> buffer;
        vector<unsigned char>::pointer dataPtr;
        int currPosition;
        bool ptrSet;
    };
}

IBuffer.cpp

    #include "StdAfx.h"
#include "IBufferPool.h"
#include <iostream>
namespace Utils
{

    IBuffer::IBuffer(void) 
        : currPosition(0), ptrSet(false) 
    {
        /*buffer.push_back('\0');*/
        SetPtr();
    }

    IBuffer::IBuffer( IBuffer* newbuff )
        :currPosition(0)
    {
    }

    IBuffer::~IBuffer(void)
    {
        if (GetPosition() != 0)
        {
            dataPtr = buffer.data();
        }

        buffer.clear();
        dataPtr = NULL;
        SetPosition(0);
        /*std::cout<<"Ibuffer Freed \n";*/
    }

    //************************************
    // Method:    WriteByte
    // FullName:  BufferPool::WriteByte
    // Access:    public 
    // Returns:   int
    // Qualifier:
    // Parameter: unsigned char data
    // Description: write data of type byte in to buffer
    //************************************
    int IBuffer::WriteByte(unsigned char data)
    {
        Lock lock(mMutex);
        buffer.push_back(data);
        SetPtr();
        return 0;
    }

    //tested OK
    int IBuffer::WriteShort(unsigned short data)
    {
        Lock lock(mMutex);
        unsigned char temp;
        //first we convert to network byte order
        /*unsigned short tmpData = HostToNetworkByteOrder(data);*/
        //lower byte

        temp = LOW_Uint16(/*tmpData*/data);
        //buffer.push_back(temp);          /// Access violation
        buffer.insert(buffer.end(),1,temp);
        temp = HI_Uint16(/*tmpData*/data);
        //buffer.push_back(temp);          /// Access violation
        buffer.insert(buffer.end(),1,temp);
        SetPtr();
        return 0;
    }

    int IBuffer::WriteShort( uint32_t position,unsigned short data )
    {
        Lock lock(mMutex);
        vector<unsigned char>::pointer pt = buffer.data();
        pt+=position;

        *pt=LOW_Uint16(data);
        pt++;
        *pt=HI_Uint16(data);
        SetPtr();
        return 0;
    }

    int IBuffer::WriteUInt(unsigned int data)
    {
        Lock lock(mMutex);
        //unsigned int and unsigned long both have 4 byte length except that unsigned long can accept floating point numbers

        vector<unsigned char> vt = Uint32ToByteArray(data);

        for each(const unsigned char &i in vt)
        {
            buffer.push_back(i);
        }
        SetPtr();
        return 0;
    }

    int IBuffer::WriteUlong( unsigned long data )
    {
        Lock lock(mMutex);
        return WriteUInt(data);
    }

    vector<unsigned char> IBuffer::Uint32ToByteArray(unsigned int x)
    {
        Lock lock(mMutex);
        vector<unsigned char> arrayOfByte(4);
        for (int i = 0; i < 4; i++)
            arrayOfByte[3 - i] = (x >> (i * 8));
        return arrayOfByte;
    }

    uint8_t* IBuffer::ToUInt8ptr()
    {
        Lock lock(mMutex);
        uint8_t* u8arr = (uint8_t*)this->buffer.data();
        size_t sz = this->buffer.size();
        u8arr[sz] = '\0';
        return u8arr;
    }

    bool IBuffer::IsEmpty()
    {
        if(!buffer.size())
            return true;
        return false;
    }


    unsigned char* IBuffer::Value()
    {
        if(!dataPtr)
        {
            dataPtr = buffer.data();
        }
        return dataPtr;
    }


    unsigned short IBuffer::HostToNetworkByteOrder(unsigned short x)
    {
        Lock lock(mMutex);
        if(IsBigEndian()){
            return x;
        }
        else{
            return ((((unsigned short)(x) & 0xff00) >> 8)       |
                (((unsigned short)(x) & 0x00ff) << 8));
        }
    }


    unsigned short IBuffer::HostToNetworkByteOrder(const void* px)
    {
        Lock lock(mMutex);
        unsigned short y = IBuffer::Convert.ToUShort((const unsigned char*)px);//IRV_TO_UINT16((const unsigned char*)px);
        return HostToNetworkByteOrder(y);
    }


    unsigned long IBuffer::HostToNetworkByteOrder(unsigned long x)
    {
        Lock lock(mMutex);
        if(IsBigEndian()){
            return x;
        }
        else{
            return ((((unsigned int)(x) & 0xff000000) >> 24)    | \
                (((unsigned int)(x) & 0x00ff0000) >> 8)     | \
                (((unsigned int)(x) & 0x0000ff00) << 8)     | \
                (((unsigned int)(x) & 0x000000ff) << 24));
        }
    }


    unsigned long IBuffer::HostToNetworkByteOrderUL(const void* px)
    {
        Lock lock(mMutex);
        unsigned long y = IBuffer::Convert.ToUShort((const unsigned char*)px);
        return HostToNetworkByteOrder(y);
    }

    unsigned long IBuffer::NetworkToHostByteOrder(const unsigned long x)
    {
        Lock lock(mMutex);
        if(IsBigEndian()){
            return x;
        }
        else{
            return ((((unsigned int)(x) & 0x000000ff) << 24)    | \
                (((unsigned int)(x) & 0x0000ff00) << 8) | \
                (((unsigned int)(x) & 0x00ff0000) >> 8)         | \
                (((unsigned int)(x) & 0xff000000) >> 24));
        }
    }

    unsigned short IBuffer::NetworkToHostByteOrder( const unsigned short x )
    {
        Lock lock(mMutex);
        if(IsBigEndian()){
            return x;
        }
        else{
            return ((((unsigned short)(x) & 0x00ff) << 8)       |
                (((unsigned short)(x) & 0xff00) >> 8));
        }
    }

    bool IBuffer::IsBigEndian(){
        /* If LITTLE_ENDIAN or BIG_ENDIAN macros have been defined in config.h ==> use them
        * otherwise ==> dyn retrieve the endianness
        */
        short word = 0x4321;
#if LITTLE_ENDIAN
        return false;
#elif BIG_ENDIAN
        return true;
#else
        return ((*(signed char *)&word) != 0x21);
#endif
    }

/*
    IBuffer IBuffer::operator=( const IBuffer &buf )
    {
        IBuffer bf;
        bf.buffer = buf.buffer;
        return bf;
    }*/

    IBuffer& IBuffer::operator=(IBuffer& Rhs)
    {
        if (this != &Rhs)
        {
            if (this->dataPtr && this->Size()>0 && this->GetPosition()>0)
            {
                delete this;
            }
            buffer=Rhs.buffer;
            dataPtr = Rhs.dataPtr;
            currPosition = Rhs.GetPosition();
        }
        return *this;

    }

    IBuffer& IBuffer::operator=( char* rhs )
    {
        Lock lock(mMutex);
        if (this->buffer.size() != 0)
        {
            this->buffer.clear();
        }
        if (this->currPosition !=0)
        {
            this->currPosition = 0;
        }
        this->Write(rhs,strlen(rhs));
        return * this;
    }

    //Move forward data pointer by one
    void IBuffer::operator++()
    {
        dataPtr++;
        SetPosition(1);
        //dataPtr+=GetPosition();
    }

    //************************************
    // Method:    ReadByte
    // FullName:  BufferPool::ReadByte
    // Access:    public 
    // Returns:   unsigned char
    // Qualifier:
    // Parameter: 
    // Description: read data of type byte and move forward data pointer
    //************************************
    unsigned char IBuffer::ReadByte()
    {
        Lock lock(mMutex);
        unsigned char ret = (*(dataPtr));
        dataPtr++;
        SetPosition(1);
        return ret;
    }

    unsigned short IBuffer::ReadShort()
    {
        Lock lock(mMutex);
        unsigned short ret=/*HostToNetworkByteOrder((unsigned char*)(dataPtr))*/IBuffer::Convert.ToUShort((unsigned char*)(dataPtr));
        dataPtr+=2;
        SetPosition(2);
        return ret;
    }

    unsigned int IBuffer::ReadUint32()
    {
        Lock lock(mMutex);
        //I DON NOT KHNOW MUST CONVERT RECIEVED MESSAGE TO LITTLE ENDIAN OR NOT?
        unsigned int ui=/*HostToNetworkByteOrderUL((unsigned char*)(dataPtr))*/Convert.ToUint32((unsigned char*)(dataPtr));
        dataPtr+=4;
        SetPosition(4);
        return ui;
    }

    unsigned long IBuffer::ReadUlong()
    {
        Lock lock(mMutex);
        return (unsigned long) ReadUint32();
    }

    unsigned char* IBuffer::Read(size_t sz)
    {
        Lock lock(mMutex);
        //return a block of data
        unsigned char* tmp = new unsigned char;
        /*Lock lock(mMutex);*/
        for (int i = 1;i<=sz;i++)
        {
            tmp[i-1] = *dataPtr++;
        }
        //dataPtr +=sz;
        tmp[sz] = '\0';
        SetPosition(sz);
        return tmp;
    }

    IBuffer::operator char*()
    {
        Lock lock(mMutex);
        char* ret = (char*)buffer.data();
        ret[buffer.size()] = '\0';
        return ret;
    }

    IBuffer::operator void*()
    {
        return (void*)buffer.data();
    }

    int IBuffer::Write(const void* data,size_t size )
    {
        Lock lock(mMutex);
        unsigned char* udata = (unsigned char*)data;
        /*Lock lock(mMutex);*/
        for (size_t i=0;i<size;i++)
        {
            buffer.push_back(*udata++);
        }
        SetPtr();
        return 0;
    }

    int IBuffer::Write( const void** data, size_t sz )
    {
        Lock lock(mMutex);
        unsigned char* udata = (unsigned char*)data;
        /*Lock lock(mMutex);*/
        for (size_t i=0;i<sz;i++)
        {
            buffer.push_back(*udata++);
        }
        SetPtr();
        return 0;
    }

    int IBuffer::Write( size_t position , const void* data,size_t sz )
    {
        Lock lock(mMutex);
        unsigned char* udata = (unsigned char*)data;
        vector<unsigned char>::pointer pt = buffer.data();
        /*Lock lock(mMutex);*/
        pt+=position;
        for (size_t i=0;i<sz;i++)
        {
            *pt=(unsigned char)(*udata++);
            pt++;
        }
        SetPtr();
        return 0;
    }

    IBuffer::operator uint8_t*()
    {
        return (uint8_t*) buffer.data();
    }

    void IBuffer::CleanUp()
    {
        if (this->Size() > 0 && this->dataPtr)
        {
            this->buffer.clear();
            this->SetPosition(0);
            this->dataPtr = NULL;
        }
    }

    void IBuffer::SetPtr()
    {
        if (!IsEmpty() /*&& !ptrSet*/)
        {
            dataPtr=buffer.data();
            ptrSet=true;
            bstate = BOB;
        }       
    }

    size_t IBuffer::Size() const
    {
        return buffer.size();
    }

    int IBuffer::SetPosition( int newpos )
    {
        //if before read function take placed then current position must be updated to new position
        //our CURSOR move forward by newpos
        if (currPosition == 0)
        {
            currPosition = newpos;
        } 
        else
        {
            currPosition += newpos;
            if (this->Size() == currPosition)
            {
                bstate = EOB;
            }
        }

        return 0;
    }

    void IBuffer::Move( int32_t count )
    {
        SetPosition(count);
        dataPtr+=count;
    }

    BufferState_t IBuffer::bstate(BOB);

これらの 2 つのクラスは最終的なものではありません。この方法でベクターをバッファーとして使用できるかどうかを確認するためのテストのみです。

編集:

HeapAlloc と HeapRealloc を使用して Ibuffer の実装を変更しましたが、この例外が発生しました

testDhcpv4.exe の 0x774a2913 (ntdll.dll) で未処理の例外: 0xC0000374: ヒープが破損しています。

私を助けてください。

ご多幸をお祈り申し上げます

4

2 に答える 2

2

コードが多すぎますが、これを見つけました

IBuffer::operator char*()
{
    Lock lock(mMutex);
    char* ret = (char*)buffer.data();
    ret[buffer.size()] = '\0';
    return ret;
}

これは未定義の動作です (配列の末尾を超えて書き込む)。

また、ベクトルとそのデータへのポインターを同時に維持しようとしているという警告もあります。ベクトルを (たとえば push_back によって) 変更すると、ポインターが無効になる可能性があることは明らかです。私はあなたがこれを間違っていると思わせるものは何も見ませんでしたが、この方法でそれを行うメリットがないため (整数を使用し、必要なときに vector::data() を呼び出すだけです)、削除することもできます.

于 2012-08-05T09:27:14.060 に答える
1

ライブラリ境界で C++ 標準ライブラリ コンテナーを公開することは、危険で壊れやすい可能性があります。

問題は、ライブラリとそのクライアントが、STL オブジェクトのストレージ レイアウトで不適切に定義されたバイナリ インターフェイスを共有していることです。STL は、設計上、その内部の多くを抽象化していません。これを行うと、パフォーマンスに深刻な影響を与えます。

これは、ライブラリとそのクライアント (共通のヘッダー ファイル セットを共有していることを思い出してください) が異なるストレージ レイアウトでコンパイルされると、あなたを苦しめる可能性があります。

これはコンパイラ (またはコンパイラのバージョン) 間で変わる可能性がありますが、本当に厄介な問題は条件付きでコンパイルされたコードです。これが問題の原因であると思われます。

Visual Studio に同梱されている STL 実装では、従来から、コンパイルされた STL オブジェクトのストレージ レイアウトを変更するデバッグ ビルドで、さまざまな形式のサニティ チェック (イテレータの使用など) が有効になっていました。

つまり、要約すると、クライアントはデバッグ リリースとしてビルドされ、ライブラリはリリース モード (またはその逆) であると思われます。

于 2012-08-05T09:24:09.747 に答える