前几天我遇到了一个问题GZipStream 没有检测到损坏的数据(甚至 CRC32 通过)?(这很可能是一个“重复”,我对这个问题有复杂的感觉。我也是在标题中添加 CRC32 的人,但回想起来,这与帖子的其余部分格格不入)。在我自己探索了一下这个问题之后,我认为这个问题远远大于其他问题最初描述的问题。
我扩展了另一个问题并使测试代码可在 LINQPad 下运行,并尝试更好地展示CRC32(循环冗余检查)问题(如果确实存在)。(由于代码只是基于原始代码的轻微修改,因此测试设置/方法可能存在缺陷,或者两者都有另一个奇怪的怪癖/PEBCAK。)
结果很奇怪,因为损坏的数据并不总是导致(任何!)引发异常。请注意,只有有时CRC32 检查似乎实际上是“工作”。可以忽略导致 index-out-of-range/bad header/bad footer 的损坏字节,因为我们可以假设这些字节会在 CRC32 检查之前杀死解压缩(这是完全可以理解的,即使 IndexOutOfRangeException 应该可能被包装由 InvalidDataException) 所以,
为什么 CRC32 检查的可靠性明显低于应有的水平?(为什么下面会出现“Invalid data (No Exception)”呢?)
由于GZip 页脚包含 CRC32和未压缩数据的长度,似乎错误检测率应该“显着更高” ——也就是说,我不希望下面出现一个失败案例,更不用说许多未检测到的损坏流. (当然,尽快检测到损坏的蒸汽是件好事:但在某些情况下,最终的保护校验和似乎完全被忽略了。)
格式为CorruptByteIndex+FailedDetections: Message
:
0+0:System.IO.InvalidDataException:GZip 标头中的幻数不正确。确保您传入的是 GZip 流。 1+0:System.IO.InvalidDataException:GZip 标头中的幻数不正确。确保您传入的是 GZip 流。 2+0: System.IO.InvalidDataException: GZip header 中指定的压缩模式未知。 3+0:良好的数据(无例外) 4+0:良好的数据(无例外) 5+0:良好的数据(无例外) 6+0:良好的数据(无例外) 7+0:良好的数据(无例外) 8+0:良好的数据(无例外) 9+0:良好的数据(无例外) 10+0:System.IO.InvalidDataException:未知的块类型。流可能已损坏。 11+1:无效数据(无异常) 12+1:System.IO.InvalidDataException:解码时发现无效数据。 13+1:System.IO.InvalidDataException:解码时发现无效数据。 14+1:System.IO.InvalidDataException:解码时发现无效数据。 15+1:System.IO.InvalidDataException:解码时发现无效数据。 16+1:System.IO.InvalidDataException:解码时发现无效数据。 17+2:无效数据(无例外) 18+2:System.IO.InvalidDataException:解码时发现无效数据。 19+2:System.IndexOutOfRangeException:Index 超出了数组的范围。 20+2:System.IndexOutOfRangeException:Index 超出了数组的范围。 21+3:无效数据(无例外) 22+3:System.IndexOutOfRangeException:Index 超出了数组的范围。 23+3:System.IndexOutOfRangeException:Index 超出了数组的范围。 24+4:无效数据(无例外) 25+4:System.IndexOutOfRangeException:Index 超出了数组的范围。 26+4:System.IndexOutOfRangeException:Index 超出了数组的范围。 27+4:System.IndexOutOfRangeException:Index 超出了数组的范围。 28+4:System.IndexOutOfRangeException:Index 超出了数组的范围。 29+5:无效数据(无例外) 30+5:System.IndexOutOfRangeException:Index 超出了数组的范围。 31+6:无效数据(无例外) 32+7:无效数据(无例外) 33+7:System.IndexOutOfRangeException:Index 超出了数组的范围。 34+7:System.IndexOutOfRangeException:Index 超出了数组的范围。 35+7:System.IndexOutOfRangeException:Index 超出了数组的范围。 36+8:无效数据(无例外) 37+8:System.IndexOutOfRangeException:Index 超出了数组的范围。 38+8:System.IndexOutOfRangeException:Index 超出了数组的范围。 39+9:无效数据(无例外) 40+9:System.IndexOutOfRangeException:Index 超出了数组的范围。 41+9:System.IndexOutOfRangeException:Index 超出了数组的范围。 42+10:无效数据(无例外) 43+10:System.IO.InvalidDataException:解码时发现无效数据。 44+10:System.IndexOutOfRangeException:Index 超出了数组的范围。 45+10:System.IO.InvalidDataException:解码时发现无效数据。 46+11:无效数据(无例外) 47+11:System.IndexOutOfRangeException:Index 超出了数组的范围。 48+11:System.IndexOutOfRangeException:Index 超出了数组的范围。 49+11:System.IndexOutOfRangeException:Index 超出了数组的范围。 50+12:无效数据(无例外) 51+12:System.IndexOutOfRangeException:Index 超出了数组的范围。 52+12:System.IndexOutOfRangeException:Index 超出了数组的范围。 53+13:无效数据(无异常) 54+13:System.IndexOutOfRangeException:Index 超出了数组的范围。 55+14:无效数据(无例外) 56+14:System.IndexOutOfRangeException:Index 超出了数组的范围。 57+15:无效数据(无例外) 58+15:System.IndexOutOfRangeException:Index 超出了数组的范围。 59+15:System.IndexOutOfRangeException:Index 超出了数组的范围。 60+16:无效数据(无例外) 61+17:无效数据(无例外) 62+18:无效数据(无例外) 63+19:无效数据(无例外) 64+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 65+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 66+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 67+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 68+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 69+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 70+19:System.IO.InvalidDataException:解码时发现无效数据。 71+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 72+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 73+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 74+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 75+19:System.IO.InvalidDataException:解码时发现无效数据。 76+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 77+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 78+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 79+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 80+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 81+19:System.IO.InvalidDataException:解码时发现无效数据。 82+19:System.IndexOutOfRangeException:Index 超出了数组的范围。 83+20:无效数据(无例外) 84+21:无效数据(无异常) 85+22:无效数据(无例外) 86+22:System.IndexOutOfRangeException:Index 超出了数组的范围。 87+23:无效数据(无例外) 88+24:无效数据(无例外) 89+25:无效数据(无例外) 90+25:System.IndexOutOfRangeException:Index 超出了数组的范围。 91+26:无效数据(无例外) 92+26:System.IO.InvalidDataException:解码时发现无效数据。 93+26:System.IndexOutOfRangeException:Index 超出了数组的范围。 94+27:无效数据(无例外) 95+27:System.IndexOutOfRangeException:Index 超出了数组的范围。 96+27:System.IndexOutOfRangeException:Index 超出了数组的范围。 97+28:无效数据(无例外) 98+28:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 99+28:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 100+29:无效数据(无例外) 101+30:无效数据(无例外) 102+31:无效数据(无异常) 103+32:无效数据(无异常) 104+32:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 105+33:无效数据(无异常) 106+34:无效数据(无异常) 107+35:无效数据(无异常) 108+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 109+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 110+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 111+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 112+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 113+35: System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 114+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 115+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 116+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 117+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 118+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 119+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 120+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 121+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 122+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 123+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 124+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 125+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 126+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 127+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 128+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 129+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 130+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 131+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 132+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 133+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 134+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 135+35: System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 136+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 137+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 138+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 139+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 140+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 141+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 142+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 143+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 144+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 145+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 146+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 147+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 148+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 149+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 150+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 151+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 152+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 153+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 154+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 155+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 156+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与从解压缩数据计算的 CRC 不匹配。 157+35:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 158+36:无效数据(无异常) 159+36:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 160+36:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 161+37:无效数据(无异常) 162+38:无效数据(无异常) 163+39:无效数据(无异常) 164+40:无效数据(无例外) 165+41:无效数据(无例外) 166+41:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 167+41:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 168+41:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 169+41:System.IO.InvalidDataException:GZip 页脚中的 CRC 与根据解压缩数据计算的 CRC 不匹配。 170+41:System.IO.InvalidDataException:GZip 页脚中的流大小与实际流大小不匹配。 171+41:System.IO.InvalidDataException:GZip 页脚中的流大小与实际流大小不匹配。 172+41:System.IO.InvalidDataException:GZip 页脚中的流大小与实际流大小不匹配。 173+41:System.IO.InvalidDataException:GZip 页脚中的流大小与实际流大小不匹配。
这是在 LINQPad 中可复制粘贴运行的测试(对于 .NET 3.5 和 4,使用“作为 C# 语句”模式):
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 System.IO.Compression.GZipStream(cmpStream, System.IO.Compression.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++)
{
var corruptData = new List<byte>(cmpData).ToArray();
// corrupt the data
corruptData[byteToCorrupt]++;
using (var decomStream = new MemoryStream(corruptData))
{
using (var hgs = new System.IO.Compression.GZipStream(decomStream, System.IO.Compression.CompressionMode.Decompress))
{
using (var reader = new StreamReader(hgs))
{
string message;
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
if (!sample.SequenceEqual(sampleOut)) {
corruptBytesNotDetected++;
message = "Invalid data (No Exception)";
} else {
message = "Good data (No Exception)";
}
}
catch(Exception ex)
{
message = (ex.GetType() + ":" + ex.Message);
}
string.Format("{0}+{1}: {2}",
byteToCorrupt, corruptBytesNotDetected, message).Dump();
}
}
}
}
这是.NET 3.5中的压缩数据(GZipStream 在“压缩”小负载方面出了名的差,但这是一个“无法修复”的问题,因为该流在技术上仍然有效):
1F 8B 08 00 00 00 00 00 04 00 ED BD 07 60 1C 49 96 25 26 2F 6D CA 7B 7F 4A F5 4A D7 E0 74 A1 08 80 60 13 24 D8 90 40 10 EC C1 88 CD E6 92 EC 1D 69 47 23 29 AB 2A 81 CA 65 56 65 5D 66 16 40 CC ED 9D BC F7 DE 7B EF BD F7 DE 7B EF BD F7 BA 3B 9D 4E 27 F7 DF FF 3F 5C 66 64 01 6C F6 CE 4A DA C9 9E 21 80 AA C8 1F 3F 7E 7C 1F 3F 22 DE CC 8B 26 A5 FF 65 E9 B4 5A 交流 EA BC 69 8A 6A 99 B6 79 D3 A6 D5 79 BA 28 A6 75 D5 54 E7 6D 3A 5E E6 6D 7A F1 83 62 15 B4 5B E4 ED BC 9A A5 D9 72 96 CE F2 FE 17 CD FF 03 5C 51 5E 27 5E 00 00 00
(而且,只是为了傻笑,在 .NET 4 中它会生成一个稍大/不同的压缩流。)
1F 8B 08 00 00 00 00 00 04 00 EC BD 07 60 1C 49 96 25 26 2F 6D CA 7B 7F 4A F5 4A D7 E0 74 A1 08 80 60 13 24 D8 90 40 10 EC C1 88 CD E6 92 EC 1D 69 47 23 29 AB 2A 81 CA 65 56 65 5D 66 16 40 CC ED 9D BC F7 DE 7B EF BD F7 DE 7B EF BD F7 BA 3B 9D 4E 27 F7 DF FF 3F 5C 66 64 01 6C F6 CE 4A DA C9 9E 21 80 AA C8 1F 3F 7E 7C 1F 3F 22 DE CC 8B 26 A5 FF 65 E9 B4 5A 交流 EA BC 69 8A 6A 99 B6 79 D3 A6 D5 79 BA 28 A6 75 D5 54 E7 6D 3A 5E E6 6D 7A F1 83 62 15 B4 5B E4 ED BC 9A A5 D9 72 96 CE F2 FE 17 CD FF 13 00 00 FF FF 5C 51 5E 27 5E 00 00 00
补充笔记:
在这种情况下,测试可能存在细微的缺陷。当 GZipStream “未能检测到损坏”(无异常)时,从 StreamReader 读取的数据为“”(空字符串):在这种情况下,为什么不ReadToEnd()
引发异常(IOException 或其他)?
因此不是GZipStream 而是这里“古怪”的 StreamReader 还是 GZipStream 仍然存在问题(因为不抛出异常)?是否有一些正确的方法来可靠地处理这个用例?(考虑当来自当前位置的输入流真的是空的时候。)