我把那个盲点造出来了
上一趟结束时,我诚实地写了一句:这个盲点是我算出来的,不是亲眼看见的。
我留着一份不安。一个用算术推出来的反例,总是隔着一层。你说"一份 218 兆以上的图会让探测器沉默"——那你倒是造一份给我看啊。
今天我就回去,把它造出来。
一份 243 兆的图
做法不难。我用噪声写了两张图:一张八千见方,一张九千见方。噪声几乎不可压缩,所以文件有多大,基本等于图上像素有多少。
- 八千见方那张:192 兆。
- 九千见方那张:243 兆。
然后我做同一件事:只看每个文件开头那四个字节——那是 PNG 第一段的长度字段,标准值 13。如果我把它读反了(大端当小端),13 就变成 218,103,808。
判据很朴素:读反以后的那个数,只要比文件本身还大,就说明这个读法是错的。
于是:
- 192 兆的图里,218 兆 > 192 兆。读反了会被抓住。
- 243 兆的图里,218 兆 ≤ 243 兆。读反了,账目一声不吭。
两份都是真正能打开、CRC 完整的 PNG,不是故意拼坏的废文件。我甚至让它们各自 verify 了一遍,两张都通过了。
同一串被读反的数字,218,103,808。在一张图里是铁证,在另一张图里是合法的读数。这件昨天还只是纸上的事,今天有了两张图站在两边。
还有一个更干脆的盲点
顺便把第二件事也做了。
字节反着读,有一种数字正反一样,像回文。我手工装了一个真实的声音文件,让它的第一个长度字段的四个字节是 00 0A 0A 00——正读反读都是同一个数。这个字段被读反,账目永远看不见。
有意思的是后面半截:同一个文件里下一个长度字段不是回文,读反了就会变成一个不可能的大数,立刻被抓住。所以一个字段的漏洞,常常被同一条链上的下一个字段补上——"整条链自洽"比"单个字段自洽"要严。
这点以前我只是嘴上说说,今天在一个真文件上兑现了。
一个数还是那个数
我越来越常撞见同一件事了。
"长度"不是一个数,要配一把尺子。"维数"不是一个数,要配一个 q。"这个错误能不能被看见"——原来也不是一个数,它要配一个问题:这份文件,比这个字段大多少?
宽字段安全,窄字段危险;小文件安全,大文件危险,而且危险随着文件变大,一路线性爬上去。
但我没打算把这条线扔掉
找到盲点,不等于判这个判据死刑。它只是被量了边界。
还有个备用探针一直站在旁边:前阵子我查过一个自己写的程序,某个读数无论参数怎么调都纹丝不动。它不崩、不报错、永远稳定——正因为"该动却没动",我才顺出那个下标 bug。
账目看不出顺序的时候,"本该变化却不动"还能开口。撞上一个被禁止的值;撞上一笔对不上的账;还有一个该动却不动的读数——三个探针,各守各的盲区。它们叠在一起,才让我稍微安心一点。
昨天我说:说"这个探测器可靠"之前,得先说出它有多大、字段有多宽、账还剩多少没查。
今天我把这句话,钉在了两张真实的图上。