Wprowadzenie

Używam poniższego kodu do kompresji GZIP w moim kodzie i doświadczam kodu segacji w bardzo konkretnych warunkach. Jestem w zasadzie całkowicie zagubiony po próbie tego rozwiązania.

Kod jest zbudowany z następującymi konfiguracjami:

  1. Linux X86-64 Ubuntu 18.04, Qt 5.12.7 Zbudowany ze źródła, przy użyciu System Zlib.
  2. Ramię Linux X86-64 Yoccto, QT 5.12.7 Zbudowany ze źródła, za pomocą System Zlib.
  3. Windows MSVC2017 64-bit, Qt 5.12.7 z oficjalnego instalatora, przy użyciu samozbudowanego Zlib, ponieważ QT nie wysycha jednego dla MSVC
  4. Windows MingW-X86-64, Qt 5.12.7 z oficjalnego instalatora, za pomocą wysłanego zlib

W konfiguracji [1-3] wezwanie testowe do kodu kompresji się powiedzie. W konfiguracji [4] Wezwanie testowe do kodu kompresji udaje się po uruchomieniu z Twórcy QT IDE. W konfiguracji [4], połączenie testowe do kodu kompresji prowadzi do Segfault, gdy tylko wywołanie deflateEnd(&cmpr_stream);, gdy zostanie zbudowany z linii poleceń Power Shell tak: 1. Ustawianie tych samych zmiennych środowiskowych w Power Shell, jak Twórca QT IDE wykorzystuje jako środowisko budowy.aclinestatus 2. Wywołanie dokładnie tych samych poleceń, takich jak skonfigurowany w IDE QT Creator IDE dla konfiguracji MingW-X86-64.

Mogę odtworzyć podobną awarię w tej konkretnej linii, nawet gdy test jest nazywany wewnątrz Qt Creator, gdy próbuję użyć rozmiaru bufora 32768 zarówno dla MSVC, jak i Mingw. MSVC zawiesza się z 16384 i Awaria Mingw z 32768, stąd #ifdef poniżej w moim kodzie dla rozmiaru bufora.

Pytania

  • Czy rzeczywisty kod kompresji poniżej wygląda w porządku? Początkowo nie został napisany i nie nauczyłem się jeszcze szczegółowo, jak prawidłowo korzystać z Zlib, jednak wiele przykładowego kodu w sieci wygląda podobnie.
  • Co może potencjalnie prowadzić do katastrofy w Zlib, gdzie ich dokumentacja stanowi, że "Lib nigdy nie powinien się zderzyć, nawet w przypadku uszkodzonego wejścia"?
  • Co może być przyczyną dla Segfault, który zdarzył się, gdy zbuduję aplikację z wiersza poleceń z MingW32 z tymi samymi zmiennymi środowiskowymi budowlanymi jak wewnątrz twórcy QT IDE, gdzie wynikowa kompilacja nie ulega awarii wykonania?

Zrobiłem zarówno kompilacje Mingw (Qt Creator z wywołaniem formularza testowego wewnątrz IDE, jak również instrukcja CLI kompilacji z wywołaniem testu z linii poleceń), aby przetestować zachowanie na czystych maszyn wirtualnych z zainstalowanymi qt i nic więcej, Aby mieć absolutne identyczne i czyste środowiska przed rozpoczęciem dowolnej z kompilacji.

Kod, który segfaults

#define BASE2_ZLIB_WINDOWSIZE 15
#define GZIP_ZLIB_WINDOWSIZE (16 + BASE2_ZLIB_WINDOWSIZE)
#define MOD_GZIP_ZLIB_CFACTOR 9
#define MOD_GZIP_ZLIB_BSIZE 8096

#ifdef _MSC_VER
#define COMPR_BUFFER_SIZE 32768
#else
#define COMPR_BUFFER_SIZE 16384
#endif

QByteArray gzipCompress(QByteArray data, int compressionlevel)
{
    char buffer[COMPR_BUFFER_SIZE];

    z_stream cmpr_stream;
    cmpr_stream.next_in = reinterpret_cast<unsigned char *>(data.data());
    cmpr_stream.avail_in = static_cast<uInt>(data.size());
    cmpr_stream.total_in = 0;
    cmpr_stream.total_out = 0;

    cmpr_stream.zalloc = Z_NULL;
    cmpr_stream.zalloc = Z_NULL;

    QByteArray compressed;

    // the actual compression work.
    if (deflateInit2(&cmpr_stream, compressionlevel, Z_DEFLATED, GZIP_ZLIB_WINDOWSIZE, MOD_GZIP_ZLIB_CFACTOR,
                        Z_DEFAULT_STRATEGY) != Z_OK)
    {
        return compressed;
    }
    // retrieve the compressed bytes blockwise
    int ret;
    do
    {
        cmpr_stream.next_out = reinterpret_cast<uint8_t *>(buffer);
        cmpr_stream.avail_out = COMPR_BUFFER_SIZE;
        ret = deflate(&cmpr_stream, Z_FINISH);

        if (static_cast<unsigned long>(compressed.size()) < cmpr_stream.total_out)
        {
            // append the block to the output string
            compressed.append(buffer, static_cast<int>(cmpr_stream.total_out) - compressed.size());
        }
    } while (ret == Z_OK);

    deflateEnd(&cmpr_stream);

    if (ret != Z_STREAM_END)
    {
        return QByteArray();
    }
    return compressed;
}

Test, dzwoniący do kodu

TEST(Compression, gzipSuccessfull)
{
    QString string_data(
        "Complex Test 256/n Data complete Lorem ipsum dolor sit amet, consetetur sadipscing elitr "
        "Lorem ipsum dolor sit amet, consetetur sadipscing elitr");
    QByteArray raw = string_data.toLatin1();
    qDebug() << "Orig size: " << raw.size();
    // Crashes in the next line
    QByteArray compressed = gzipCompress(raw);
    qDebug() << "Compressed size: " << compressed.size();
    QByteArray uncompressed = gzipDecompress(compressed);
    qDebug() << "Uncompressed size: " << uncompressed.size();
    QString restored = QString::fromLatin1(uncompressed);
    ASSERT_TRUE(string_data == restored);
}
0
darkmattercoder 22 październik 2020, 16:33

1 odpowiedź

Najlepsza odpowiedź

Nie jestem pewien, czy to powoduje twoją segfault, ale musisz zainicjować zfree. Wygląda jak błąd kopii / wklejania w kodzie, ponieważ dwukrotnie inicjalizujesz zalloc. Powinien być:

cmpr_stream.zalloc = Z_NULL;
cmpr_stream.zfree = Z_NULL;

I być kompletnym:

cmpr_stream.opaque = Z_NULL;

Twoje obliczenia, ile dołączania, ale jest to sposób ronda, aby to zrobić, i może mieć problem przenośności, jeśli long wynosi 32 bitów. W takim przypadku total_out nie stanowiłoby całkowitej kwoty, gdy więcej niż 4 GB. Zamiast tego powinieneś używać bezpośredniego wyniku z deflate():

    if (cmpr_stream.avail_out < COMPR_BUFFER_SIZE)
    {
        // append the block to the output string
        compressed.append(buffer, COMPR_BUFFER_SIZE - cmpr_stream.avail_out);
    }

Ponieważ parametr do append jest int zamiast unsigned, powinieneś upewnić się, że COMPR_BUFFER_SIZE pasuje do int.

1
Mark Adler 22 październik 2020, 15:56