简体   繁体   English

libzip zip_source_t 缓冲区已被释放,即使我告诉 libzip 不要这样做

[英]libzip zip_source_t buffer is freed even tho I told libzip not to

I face an issue with libzip and I don't know if I did something wrong of if it's a bug in the library.我遇到了 libzip 的问题,我不知道我是否做错了是否是库中的错误。

First I create an archive with zip_source_buffer_create() from a zip file on disk (don't ask why I don't open the file directly).首先,我使用zip_source_buffer_create()从磁盘上的 zip 文件创建一个存档(不要问我为什么不直接打开该文件)。

I set the freep flag to 0 which is supposed to mean I will handle the buffer freeing myself and it's not lipzip's job.我将freep标志设置为 0,这应该意味着我将处理释放自己的缓冲区,这不是 lipzip 的工作。

It works if I don't add a file to the archive.如果我不将文件添加到存档中,它会起作用。

If I add a file to the archive, when I delete the buffer libzip has already free'd it and my application crashes.如果我将文件添加到存档中,当我删除缓冲区时,libzip 已经释放它并且我的应用程序崩溃了。 Is that a bug in the library?这是图书馆的错误吗?

I wrote this self-explanatory mcve.我写了这个不言自明的 mcve。 With AUTO_FREE_BUF=0 and ADD_FILE=1 , the application crashes.使用AUTO_FREE_BUF=0ADD_FILE=1 ,应用程序崩溃。 Every other (3) cases work.每隔 (3) 个案例工作。

libzip version: 1.7.3 libzip 版本:1.7.3

#include <zip.h>
#include <string>
#include <filesystem>
#include <fstream>
#include <iostream>

#define AUTO_FREE_BUF 0 //1-0
#define ADD_FILE 1 //1-0

int main(int argc, char** argv) {

    const std::string add_file = "file.txt";
    const std::string zip_file = "zipfile.zip";

    size_t sz = std::filesystem::file_size(zip_file);
    char* buf = new char[sz];
    {
        std::ifstream ifs{ zip_file, std::ios::binary };
        ifs.read(buf, sz);
        ifs.close();

        zip_error_t ze;
        zip_error_init(&ze);
        zip_source_t* zs = zip_source_buffer_create(buf, sz, AUTO_FREE_BUF, &ze);
        if (zs == nullptr) {
            zip_error_fini(&ze);
            return -1;
        }

        zip_error_init(&ze);
        zip_t* z = zip_open_from_source(zs, NULL, &ze);
        if (z == nullptr) {
            zip_error_fini(&ze);
            return -1;
        }
#if ADD_FILE == 1
        //add file
        {
            zip_source_t* file = zip_source_file(z, add_file.c_str(), 0, -1);
            if (file == nullptr)
                return -1;

            zip_int64_t add = zip_file_add(z, add_file.c_str(), file, ZIP_FL_ENC_GUESS);
            if (add == -1)
                return -1;
        }
#endif
        zip_error_fini(&ze);
        zip_source_keep(zs);
        int close = zip_close(z);
        if (close == -1)
            return -1;

        //write back archive to disk
        //..

#if AUTO_FREE_BUF == 1
        zip_source_free(zs);
#else
        zip_source_free(zs); //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
        delete[] buf;
#endif
    }

    return 0;
}

I've installed libzip from sources (as described in the install.md) and reworked your source.我已经从源代码安装了 libzip(如 install.md 中所述)并重新编写了您的源代码。 I've added a textfile (with "Hello world") and a zip file with one text file inside.我添加了一个文本文件(带有“Hello world”)和一个 zip 文件,里面有一个文本文件。

I'm not getting errors.我没有收到错误。 (However, nothing is added to the zip file, so that's also not right) (但是,zip 文件中没有添加任何内容,所以这也不对)

(ignore the exception parameters commented out:P ) (忽略注释掉的异常参数:P)

#include <zip.h>

#include <exception>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>

static constexpr auto add_file{"file.txt"};
static constexpr auto zip_file{"zipfile.zip"};
static constexpr auto AUTO_FREE_BUF{false};  //1-0
static constexpr auto ADD_FILE{true};        //1-0

class MsgException : public std::exception {
   public:
    MsgException(std::string message) : _message(message) {}
    char const *what() const noexcept override { return _message.c_str(); }

   private:
    std::string const _message;
};

int main() {
    std::cout << "Started main\n";
    auto const sz = std::filesystem::file_size(zip_file);
    auto *const buf = new char[sz];
    {
        std::ifstream ifs{zip_file, std::ios::binary};
        ifs.read(buf, sz);
    }
    std::cout << "Zipfile read\n";

    zip_error_t ze;
    try {
        zip_error_init(&ze);
        auto *zs = zip_source_buffer_create(buf, sz, AUTO_FREE_BUF ? 1 : 0, &ze);
        if (zs == nullptr) throw MsgException("Can't create source: %s." /*, zip_error_strerror(&ze)*/);

        //zip_error_init(&ze);
        auto *z = zip_open_from_source(zs, 0, &ze);
        if (z == nullptr) throw MsgException("Can't open zip from source: %s." /*, zip_error_strerror(&ze)*/);

        if (ADD_FILE) {
            //add file
            auto *file = zip_source_file(z, add_file, 0, -1);
            if (file == nullptr) throw MsgException("Can't create data source from file '%s': %s." /*, add_file, zip_strerror(z)*/);

            if (zip_file_add(z, add_file, file, ZIP_FL_ENC_GUESS) < 0) {
                //? zip_source_free(file);
                throw MsgException("Can't file to zip archive: %s." /*, zip_strerror(z)*/);
            }
        }

        zip_source_keep(zs);

        if (zip_close(z) < 0) throw MsgException("Can't close zip archive: %s." /*, zip_strerror(z)*/);

        //write back archive to disk
        //..

        zip_source_free(zs);  //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
    } catch (std::exception &e) {
        std::cerr << "Program aborted with error: " << e.what() << '\n';
    }
    zip_error_fini(&ze);
    if (!AUTO_FREE_BUF) delete[] buf;

    std::cout << "Finished main\n";
    return 0;
}

build using使用构建

g++ -std=c++17 main.cpp -lzip -o main

Edit: Added some RAII编辑:添加了一些 RAII

#include <zip.h>

#include <exception>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <string>
#include <vector>

static constexpr auto add_file{ "file.txt" };
static constexpr auto zip_file{ "zipfile.zip" };
static constexpr auto AUTO_FREE_BUF{ false };  //1-0
static constexpr auto ADD_FILE{ true };        //1-0

class MsgException : public std::exception {
public:
    MsgException(std::string message) : _message(message) {}
    char const* what() const noexcept override { return _message.c_str(); }

private:
    std::string const _message;
};

//RAII
class ZipError {
public:
    ZipError() { zip_error_init(&ze); }
    ~ZipError() { zip_error_fini(&ze); }
    zip_error* operator&() { return &ze; }
    // actually probably need to delete copy/move constructor/assignment etc...
private:
    zip_error_t ze;
};

int main() {
    std::cout << "Started main\n";
    std::vector<char> buf(std::filesystem::file_size(zip_file)); // RAII
    {
        std::ifstream ifs{ zip_file, std::ios::binary };
        ifs.read(buf.data(), buf.size());
    }
    std::cout << "Zipfile read\n";

    
    try {
        ZipError ze;
        auto* zs = zip_source_buffer_create(buf.data(), buf.size(), AUTO_FREE_BUF ? 1 : 0, &ze);
        if (zs == nullptr) throw MsgException("Can't create source: %s." /*, zip_error_strerror(&ze)*/);

        //zip_error_init(&ze);
        auto* z = zip_open_from_source(zs, 0, &ze);
        if (z == nullptr) throw MsgException("Can't open zip from source: %s." /*, zip_error_strerror(&ze)*/);

        if (ADD_FILE) {
            //add file
            auto* file = zip_source_file(z, add_file, 0, -1);
            if (file == nullptr) throw MsgException("Can't create data source from file '%s': %s." /*, add_file, zip_strerror(z)*/);

            if (zip_file_add(z, add_file, file, ZIP_FL_ENC_GUESS) < 0) {
                //? zip_source_free(file);
                throw MsgException("Can't file to zip archive: %s." /*, zip_strerror(z)*/);
            }
        }

        zip_source_keep(zs);

        if (zip_close(z) < 0) throw MsgException("Can't close zip archive: %s." /*, zip_strerror(z)*/);

        //write back archive to disk
        //..

        zip_source_free(zs);  //<-is supposed to NOT free the buffer (with freep=0) but sometimes does
    }
    catch (std::exception& e) {
        std::cerr << "Program aborted with error: " << e.what() << '\n';
    }
    
    std::cout << "Finished main\n";
    return 0;
}

Don't handle zip archives created with WinRar with libzip............... (create them with libzip)不要处理使用 WinRar 和 libzip 创建的 zip 档案......(使用 libzip 创建它们)

After (hours now) of investigation I think I understand the rationale behind this and I think libzip doesn't behave as it should.经过(现在几个小时)的调查,我想我理解这背后的基本原理,我认为 libzip 的行为不应该如此。

zip_source_free(src) decrements the refcount of src and if it reaches 0 the src object is free'd. zip_source_free(src)减少src的引用计数,如果它达到 0,则src object 被释放。 Additionnaly if src was created with freep=1 the associated data buffer is also free'd.此外,如果src是使用freep=1创建的,则关联的数据缓冲区也会被释放。 If freep=0 then the data buffer should not be freed.如果freep=0则不应释放数据缓冲区。

But in this case and in some occasions the buffer IS free'd and that's probably unintended.但是在这种情况下,在某些情况下,缓冲区是空闲的,这可能是无意的。

In the mcve above, adding a file to the archive makes libzip free the data buffer even tho the source was created with freep=0 .在上面的 mcve 中,将文件添加到存档中会使 libzip 释放数据缓冲区,即使源是使用freep=0创建的。 If no file is added to the archive then the buffer is not free'd as expected and I can safely delete[] it.如果没有文件被添加到存档中,那么缓冲区不会像预期的那样被释放,我可以安全地delete[]它。

This is not a solution to my question tho.这不是我的问题的解决方案。 I noticed src.zip_err=18 [Invalid argument] in the mcve after adding the file so there might be something wrong with how the file is added - I haven't found what't wrong with it.添加文件后,我在 mcve 中注意到src.zip_err=18 [Invalid argument] ,因此添加文件的方式可能有问题 - 我还没有发现它有什么问题。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM