GZipStream не обнаруживает поврежденные данные (даже проходы CRC32)?

Я использую GZipStream для сжатия / распаковки данных. Я выбрал это вместо DeflateStream, так как в документации говорится, что GZipStream также добавляет CRC для обнаружения поврежденных данных, что является еще одной функцией, которую я хотел. Мои "положительные" модульные тесты работают хорошо: я могу сжать некоторые данные, сохранить сжатый байтовый массив и затем снова успешно распаковать его. Сообщение о проблеме сжатия и распаковки.NET GZipStream помогло мне понять, что мне нужно закрыть GZipStream перед доступом к сжатым или распакованным данным.

Затем я продолжил писать "отрицательный" модульный тест, чтобы убедиться, что поврежденные данные могут быть обнаружены. Ранее я использовал пример для класса GZipStream из MSDN, чтобы сжать файл, открыть сжатый файл с помощью текстового редактора, изменить байт, чтобы испортить его (как будто открытие его с помощью текстового редактора не было достаточно плохим!), Сохранить затем распакуйте его, чтобы убедиться, что я получил InvalidDataException, как и ожидалось.

Когда я написал модульный тест, я выбрал произвольный байт для повреждения (например, compressDataBytes[50] = 0x99) и получил InvalidDataException. Все идет нормально. Мне было любопытно, поэтому я выбрал другой байт, но, к своему удивлению, я не получил исключения. Это может быть хорошо (например, я случайно ударил неиспользуемый байт в блоке данных), если данные все еще могут быть успешно восстановлены. Тем не менее, я также не получил правильные данные обратно!

Чтобы быть уверенным, что "это был не я", я взял очищенный код из нижней части проблемы сжатия и распаковки.NET GZipStream и изменил его, чтобы последовательно повреждать каждый байт сжатых данных до тех пор, пока он не будет распакован должным образом. Вот изменения (обратите внимание, что я использую тестовую среду Visual Studio 2010):

// successful compress / decompress example code from:
//    https://stackru.com/questions/1590846/net-gzipstream-compress-and-decompress-problem
[TestMethod]
public void Test_zipping_with_memorystream_and_corrupting_compressed_data()
{
   const string sample = "This is a compression test of microsoft .net gzip compression method and decompression methods";
   var encoding = new ASCIIEncoding();
   var data = encoding.GetBytes(sample);
   string sampleOut = null;
   byte[] cmpData;

   // Compress 
   using (var cmpStream = new MemoryStream())
   {
      using (var hgs = new GZipStream(cmpStream, CompressionMode.Compress))
      {
         hgs.Write(data, 0, data.Length);
      }
      cmpData = cmpStream.ToArray();
   }

   int corruptBytesNotDetected = 0;

   // corrupt data byte by byte
   for (var byteToCorrupt = 0; byteToCorrupt < cmpData.Length; byteToCorrupt++)
   {
      // corrupt the data
      cmpData[byteToCorrupt]++;

      using (var decomStream = new MemoryStream(cmpData))
      {
         using (var hgs = new GZipStream(decomStream, CompressionMode.Decompress))
         {
            using (var reader = new StreamReader(hgs))
            {
               try
               {
                  sampleOut = reader.ReadToEnd();

                  // if we get here, the corrupt data was not detected by GZipStream
                  // ... okay so long as the correct data is extracted
                  corruptBytesNotDetected++;

                  var message = string.Format("ByteCorrupted = {0}, CorruptBytesNotDetected = {1}",
                     byteToCorrupt, corruptBytesNotDetected);

                  Assert.IsNotNull(sampleOut, message);
                  Assert.AreEqual(sample, sampleOut, message);
               }
               catch(InvalidDataException)
               {
                  // data was corrupted, so we expect to get here
               }
            }
         }
      }

      // restore the data
      cmpData[byteToCorrupt]--;
   }
}

Когда я запускаю этот тест, я получаю:

Assert.AreEqual failed. Expected:<This is a compression test of microsoft .net gzip compression method and decompression methods>. Actual:<>. ByteCorrupted = 11, CorruptBytesNotDetected = 8

Таким образом, это означает, что в действительности было 7 случаев, когда повреждение данных не имело значения (строка была успешно восстановлена), но повреждение 11 байта не вызвало исключение и не восстановило данные.

Я что-то упускаю или делаю что-то не так? Кто-нибудь может понять, почему поврежденные сжатые данные не обнаруживаются?

1 ответ

В формате gzip имеется 10-байтовый заголовок, для которого последние семь байтов могут быть изменены без возникновения ошибки распаковки. Итак, семь случаев, которые вы отметили без коррупции, ожидаются.

Должно быть крайне редко, чтобы не обнаружить ошибку с повреждением где-либо еще в потоке. Большую часть времени декомпрессор обнаруживает ошибку в формате сжатых данных, даже не доходя до точки проверки crc. Если он доходит до точки проверки crc, эта проверка должна почти всегда завершаться неудачно с поврежденным входным потоком. ("Почти все время" означает вероятность около 1 - 2^-32.)

Я только что попробовал это (в C с zlib), используя вашу примерную строку, которая создает 84-байтовый поток gzip. Увеличение каждого из 84 байтов, оставляя остаток таким же, как вы, привело к: двум неверным проверкам заголовка, одному неверному методу сжатия, семи успехам, одному неверному типу блока, четырем установленным неверным расстояниям, семи неверным длинам кода, четырем отсутствующим конец блока, 11 недопустимых повторов битовой длины, три недопустимых повторения битовой длины, два недопустимых повторения битовой длины, два неожиданных конца потока, 36 неверных проверок данных (это фактическая ошибка CRC) и четыре неверных проверки длины (еще одна проверка в формате gzip для правильной длины несжатых данных). Ни в одном случае поврежденный сжатый поток не был обнаружен.

Так что где-то должна быть ошибка, либо в вашем коде, либо в классе.

Обновить:

Похоже, что в классе есть ошибка.

Примечательно (или, может быть, не замечательно), что Microsoft пришла к выводу, что они не исправят эту ошибку!

Другие вопросы по тегам