1

我有使用 zLib 和 Google 协议缓冲区的代码。问题是,valgrind 在一个同时使用两者的类中检测到无效释放。

==1419== Invalid free() / delete / delete[] / realloc()
==1419==    at 0x4A06F1C: operator delete[](void*) (vg_replace_malloc.c:515)
==1419==    by 0x4C3928F: openstreetmap::cOsmBlob::~cOsmBlob() (cOsmBlob.cc:129)
==1419==    by 0x4045DF: void __gnu_cxx::new_allocator<openstreetmap::cOsmBlob>::destroy<openstreetmap::cOsmBlob>(openstreetmap::cOsmBlob*) (new_allocator.h:114)
==1419==    by 0x404005: std::deque<openstreetmap::cOsmBlob, std::allocator<openstreetmap::cOsmBlob> >::pop_front() (stl_deque.h:1407)
==1419==    by 0x403D39: std::queue<openstreetmap::cOsmBlob, std::deque<openstreetmap::cOsmBlob, std::allocator<openstreetmap::cOsmBlob> > >::pop() (stl_queue.h:240)
==1419==    by 0x403BD6: threading::cFifoBuffer<openstreetmap::cOsmBlob>::~cFifoBuffer() (storage.h:86)
==1419==    by 0x403A15: main (main.cc:40)
==1419==  Address 0x50be390 is 752 bytes inside a block of size 7,152 free'd
==1419==    at 0x4A077E6: free (vg_replace_malloc.c:446)
==1419==    by 0x37A6209B3A: inflateEnd (inflate.c:1261)
==1419==    by 0x4C39156: openstreetmap::cOsmBlob::cOsmBlob(std::basic_ifstream<char, std::char_traits<char> >*) (cOsmBlob.cc:116)
==1419==    by 0x4C5B157: openstreetmap::cPbfPipelineFileReader::run(void*) (cPbfPipelineFileReader.cc:158)
==1419==    by 0x37A5A07D14: start_thread (pthread_create.c:308)
==1419==    by 0x37A4EF246C: clone (clone.S:114)
==1419== 
==1419== Invalid free() / delete / delete[] / realloc()
==1419==    at 0x4A06F1C: operator delete[](void*) (vg_replace_malloc.c:515)
==1419==    by 0x4C3928F: openstreetmap::cOsmBlob::~cOsmBlob() (cOsmBlob.cc:129)
==1419==    by 0x4045DF: void __gnu_cxx::new_allocator<openstreetmap::cOsmBlob>::destroy<openstreetmap::cOsmBlob>(openstreetmap::cOsmBlob*) (new_allocator.h:114)
==1419==    by 0x404604: std::deque<openstreetmap::cOsmBlob, std::allocator<openstreetmap::cOsmBlob> >::_M_pop_front_aux() (deque.tcc:520)
==1419==    by 0x404027: std::deque<openstreetmap::cOsmBlob, std::allocator<openstreetmap::cOsmBlob> >::pop_front() (stl_deque.h:1411)
==1419==    by 0x403D39: std::queue<openstreetmap::cOsmBlob, std::deque<openstreetmap::cOsmBlob, std::allocator<openstreetmap::cOsmBlob> > >::pop() (stl_queue.h:240)
==1419==    by 0x403BD6: threading::cFifoBuffer<openstreetmap::cOsmBlob>::~cFifoBuffer() (storage.h:86)
==1419==    by 0x403A15: main (main.cc:40)
==1419==  Address 0x5eb45d0 is not stack'd, malloc'd or (recently) free'd

我不明白为什么地址没有正确对齐以及为什么块被释放两次。

#define MAX_BLOB_HEADERSIZE (64 * 1024)
#define MAX_BLOB_DATASIZE   (32 * 1024 * 1024)

cOsmBlob::cOsmBlob(std::ifstream *stream) {
    char headerLength[4], *buffer;
    pbf::BlobHeader header;
    uint32_t length;
    pbf::Blob blob;
    int retval;

    if (stream == NULL)
            throw std::exception();

    stream->read(headerLength, 4);
    length = ntohl(*((uint32_t*)&headerLength));
    if (length == 0 || length > MAX_BLOB_HEADERSIZE) {
        cerr << "cOsmBlob: invalid blob header size" << endl;
        throw exception();
    }

    /* read the BlobHeader */
    buffer = new char[length];
    stream->read(buffer, length);
    if (header.ParseFromArray(buffer, length) == false) {
        cerr << "cOsmBlob: unable to read BlobHeader" << endl;
        delete[] buffer;
        throw exception();
    }
    delete[] buffer;

    /* read the Blob */
    if (header.datasize() == 0 || header.datasize() > MAX_BLOB_DATASIZE) {
        cerr << "cOsmBlob: invalid header->datasize()" << endl;
        throw exception();
    }
    buffer = new char[header.datasize()];
    stream->read(buffer, header.datasize());
    if (blob.ParseFromArray(buffer, header.datasize()) == false) {
        cerr << "cOsmBlob: unable to read Blob" << endl;
        delete[] buffer;
        throw exception();
    }
    delete[] buffer;

    if (header.type() == "OSMHeader") {
        this->type = OSM_HEADER;
    }
    else if (header.type() == "OSMData") {
        this->type = OSM_DATA;
    }
    else {
        cerr << "cOsmBlob: Unknown BlobHeader type" << endl;
        throw exception();
    }

    /* extract data */
    this->size = 0;
    this->rawdata = NULL;
    if (blob.has_raw() == true) {
        this->rawdata = new char[blob.raw().length()];
        memcpy(this->rawdata, blob.raw().data(), blob.raw().length());
        this->size = blob.raw().length();
    }
    else if (blob.has_zlib_data() == true) {
        z_stream zlibStream;

        this->rawdata = new char[blob.raw_size()];
        zlibStream.next_in =
               (Bytef*)const_cast<char*>(blob.zlib_data().data());
        zlibStream.avail_in = blob.zlib_data().size();
        zlibStream.next_out = (Bytef*)this->rawdata;
        zlibStream.avail_out = blob.raw_size();
        zlibStream.zalloc = Z_NULL;
        zlibStream.zfree = Z_NULL;
        zlibStream.opaque = Z_NULL;

        if (inflateInit(&zlibStream) != Z_OK) {
            cerr << "cOsmBlob: " <<
                    "Unknown or unsupported ZLib version of blob's data" <<
                    endl;
            delete[] buffer;
            throw exception();
        }
        retval = inflate(&zlibStream, Z_FINISH);
        if (retval != Z_OK && retval != Z_STREAM_END) {
            cerr << "cOsmBlob: Unable to decompress blob's data" << endl;
            delete[] buffer;
            throw exception();
        }
        if (inflateEnd(&zlibStream) != Z_OK) {
            cerr << "cOsmBlob: " <<
                    "Unable to finalize decompression of blob's data" <<  endl;
            delete[] buffer;
            throw exception();
        }

        this->size = blob.raw_size();
    }
}

cOsmBlob::~cOsmBlob(void) {
    if (this->rawdata != NULL)
            delete[] this->rawdata;
    this->rawdata = NULL;
}
4

1 回答 1

2

从您的评论来看,您的班级似乎违反了三原则

由于您将类的实例存储在容器中,因此这涉及复制它们。在没有自定义复制构造函数和赋值运算符的情况下,这将使用编译器生成的。后者只会获取buffer指针的副本。当原始对象超出范围时,其缓冲区为delete[]d,副本带有一个悬空指针。当副本超出范围时,它将再次尝试delete[]指向同一个指针,从而导致您看到的错误。

您需要提供一个复制构造函数和一个复制赋值运算符。

于 2013-03-23T09:20:10.830 回答