【问题标题】:firefox cache hash key generation algorithm bugfirefox缓存散列密钥生成算法错误
【发布时间】:2010-10-14 11:08:46
【问题描述】:

a bug in Firefox(即使在新的测试版和雷区版本中)由于在其缓存哈希中创建密钥的算法而阻止缓存某些文件。 Here is a link to the source code of the function

我想确保可以缓存我网站的所有文件。但是,我不明白为什么他们的散列函数无法为不同的 url 创建唯一键。我希望有人可以用伪代码或 java 描述这个 mal 函数。

最好为开发人员创建一个实用程序,以确保在修复此错误之前唯一的 url。


编辑:有一些非常有用的答案,但是,我需要更多的分步帮助来创建一个实用程序来检查这些缓存混淆。获得一些可以重现 Firefox 正在创建的密钥的 java 代码会很棒。因此,在这个问题上开悬赏。


编辑 2: 这是一个部分工作的 Java 端口(使用 processing 编写)。注意底部的测试;前三个按预期工作,但其他人没有。我怀疑有关已签名/未签名整数的某些事情。有什么建议吗?

//
// the bad collision function
// http://mxr.mozilla.org/mozilla/source/netwerk/cache/src/nsDiskCacheDevice.cpp#240
//

//248 PLDHashNumber
//249 nsDiskCache::Hash(const char * key)
//250 {
//251     PLDHashNumber h = 0;
//252     for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
//253         h = PR_ROTATE_LEFT32(h, 4) ^ *s;
//254     return (h == 0 ? ULONG_MAX : h);
//255 }

//
//  a java port...
//

String getHash( String url )
{

//get the char array for the url string
char[] cs = getCharArray( url );

int h = 0;

//for (const PRUint8* s = (PRUint8*) key; *s != '\0'; ++s)
for ( int i=0; i < cs.length; i++ )
{  h = PR_ROTATE_LEFT32(h, 4) ^ cs[i];
}

//looks like the examples above return something in hex.
//if we get matching ints, that is ok by me.
//but for fun, lets try to hex the return vals?
String hexVal = hex( h );
return hexVal;
}

char[] getCharArray( String s )
{
  char[] cs = new char[s.length()];
  for (int i=0; i<s.length(); i++)
  { 
    char c = s.charAt(i);
    cs[i] = c;
  } 

  return cs;
}

//
// how to PR_ROTATE_LEFT32
//

//110 /*
//111 ** Macros for rotate left and right. The argument 'a' must be an unsigned
//112 ** 32-bit integer type such as PRUint32.
//113 **
//114 ** There is no rotate operation in the C Language, so the construct
//115 ** (a << 4) | (a >> 28) is frequently used instead. Most compilers convert
//116 ** this to a rotate instruction, but MSVC doesn't without a little help.
//117 ** To get MSVC to generate a rotate instruction, we have to use the _rotl
//118 ** or _rotr intrinsic and use a pragma to make it inline.
//119 **
//120 ** Note: MSVC in VS2005 will do an inline rotate instruction on the above
//121 ** construct.
//122 */
//...
//128 #define PR_ROTATE_LEFT32(a, bits) _rotl(a, bits)


//return an int (32 bit).  what do we do with the 'bits' parameter?  ignore?
int PR_ROTATE_LEFT32( int a, int bits )
{    return (a << 4) | (a >> (32-bits)); 
}

//
// examples of some colliding hashes
// https://bugzilla.mozilla.org/show_bug.cgi?id=290032#c5
//

//$ ./hashit "ABA/xxx.aba"
//8ffac222
//$ ./hashit "XyZ/xxx.xYz"
//8ffac222
//$ ./hashit "CSS/xxx.css"
//8ffac222
//$ ./hashit "JPG/xxx.jpg"
//8ffac222

//$ ./hashit modules_newsfeeds/MenuBar/MenuBar.css
//15c23729
//$ ./hashit modules_newsfeeds/ListBar/ListBar.css
//15c23729

//$ ./hashit modules_newsfeeds/MenuBar/MenuBar.js
//a15c23e5
//$ ./hashit modules_newsfeeds/ListBar/ListBar.js
//a15c23e5



//
// our attempt at porting this algorithm to java...
//

void setup( )
{

String a = "ABA/xxx.aba";
String b = "CSS/xxx.css";
String c = "CSS/xxx.css";
String d = "JPG/xxx.jpg";

println( getHash(a) ); //yes 8ffac222
println( getHash(b) ); //yes 8ffac222
println( getHash(c) ); //yes 8ffac222
println( getHash(d) ); //no [??] FFFFFF98, not 8ffac222

println( "-----" );

String e = "modules_newsfeeds/MenuBar/MenuBar.css";
String f = "modules_newsfeeds/ListBar/ListBar.css";

println( getHash(e) ); //no [??] FFFFFF8C, not 15c23729
println( getHash(f) ); //no [??] FFFFFF8C, not 15c23729

println( "-----" );

String g = "modules_newsfeeds/MenuBar/MenuBar.js";
String h = "modules_newsfeeds/ListBar/ListBar.js";

println( getHash(g) ); //yes [??] FFFFFF8C, not a15c23e5
println( getHash(h) ); //yes [??] FFFFFF8C, not a15c23e5

}

【问题讨论】:

  • 老实说,我认为您完全担心这一点。您是否遇到了某种问题,或者这一切都是过早的优化?
  • 对问题的进一步解释:需要制定策略以确保正确缓存数千个文件。现在,他们不是。想要预处理所有文件名以确保它们可以缓存。

标签: java c++ algorithm firefox hash


【解决方案1】:

根据我仅阅读 bugzilla 条目的理解,当出现两个不同的问题时,该错误就会显现:

  1. 他们的哈希算法会为“足够相似”的 url 生成冲突。从“足够相似”的错误来看,似乎意味着每 4 个字符(或者可能是 8 个)的 url 是相同的,并且
  2. 他们处理哈希冲突的逻辑失败了,因为他们还没有将具有相同哈希值的前一个 url 刷新到磁盘。

所以基本上,如果你的页面有两个非常相似的 url,这可能会发生在某些版本的 Firefox 上。我希望它通常不会发生在不同的页面上,因为那时 FF 将有时间将条目刷新到磁盘以避免时间问题。

因此,如果您有多个资源(脚本、图像等)都从同一页面加载,请确保它们包含 9 个完全不同的字符。您可以确保这一点的一种方法是在查询字符串(您忽略)中附加随机数据位,例如:

【讨论】:

  • 是的,我在应该是位的地方读取字节,然后在心里将其转换为字符。下面的其他人对哈希算法有很好的解释。
  • 查询字符串的建议很好,但希望确保我的文件的唯一 url 作为预处理。
  • 另外,在运行时添加随机查询字符串需要在某处缓存该随机查询字符串,而不是开发一个不冲突的模式。
【解决方案2】:

首先,您不能将所有字符串唯一地散列为整数(显然,字符串比(固定大小)整数多,因此必须存在冲突)。您可以拥有一个可以保存所有数据集(例如所有文件)的哈希表,但要获得它,您需要更改哈希表的代码,而不是哈希函数。

其次,我看到你发布的哈希函数有问题,在这部分:

PR_ROTATE_LEFT32(h, 4)

如果它确实对h 进行了旋转(我没有对此进行检查),则旋转 4 意味着交换了两个 8 字节(我假设为 32 位哈希)部分的字符串(例如 xxxxxxxxyyyyyyyy 与.yyyyyyyyxxxxxxxx) 将具有相等的哈希值。如果您将其更改为与哈希大小相对质数的值(例如 5),这只会发生在长度为 32 的交换部分。

【讨论】:

  • 我认为他要问的问题是“我怎样才能解决这个糟糕的哈希函数”,而不是“我怎样才能建立一个更好的哈希函数”
【解决方案3】:

以下是算法的工作原理:

initialize hash to 0
for each byte
    shift hash 4 bits to left (with rotate)
    hash = hash XOR character

视觉上(16 位版本):

00110000             = '0'
    00110001         = '1'
        00110010     = '2'
            00110011 = '3'
0100            0011 = '4'
00110101             = '5'
====================
01000110001000010000  (and then this will be 'rotated'
                       so that it lines up with the end)
giving:
        00100001000001000110

这意味着如果你有相同长度的字符串并且大部分相同,那么至少在一种情况下,一个 char 的低 4 位和下一个 char 的高 4 位相互异或必须是唯一的.但是,将 32 位数字粘贴到表中的方法可能会更弱,这意味着它要求字符串中特定位置(mod 8 字符)的 lower4 xor upper4 是唯一的。

【讨论】:

    【解决方案4】:

    您显然误解了真正的错误。当然,由于哈希算法的选择非常糟糕,会出现哈希冲突。但即使 hash(x)=1 也不会导致所描述的问题。它只会将 O(1) 查找转换为 O(N) 链表搜索通过第一个存储桶。

    真正的问题是 Firefox 无法处理哈希冲突。因此,它需要所有 URL 的完美散列。不幸的是,“所有 URL”是您无法控制的集合。

    【讨论】:

    • 我至少可以确保我网站的“所有 url”子集不会与我网站的预处理实用程序发生冲突。
    【解决方案5】:

    这个错误是我网站的一个主要问题:http://worldofsolitaire.com

    很久以前,我通过在 .htaccess 文件中使用条件规则来解决这个问题,该规则将禁用 Firefox 用户在站点上的所有图像缓存。这是一件可怕的事情,但当时我无法找到 Firefox 中的错误,让网站稍微慢一点总比显示重复/损坏的图像要好。

    当我在链接的错误中读到它已在最新的 Firefox 版本中修复时,我在 2009 年 4 月 19 日(昨天)更改了条件,只为 Firefox 2 用户禁用缓存。

    几个小时后,我收到了 10 多封来自 Firefox 3 用户的电子邮件(确认),他们看到了重复的图像。所以这个问题在 Firefox 3 中仍然是一个问题。

    我决定创建一个简单的 Linux 测试程序,让我可以检查 URL 以查看它们是否生成相同的缓存哈希键。

    在任何Linux系统中编译:g++ -o ffgenhash ffgenhash.cpp

    这是代码(保存到文件 ffgenhash.cpp)

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    
    #define ULONG_MAX 0xFFFFFFFF
    #define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
    
    unsigned long ffgenhash(const char * key)
    {
        unsigned long h=0;
    
        for(const unsigned char * s = (unsigned char *) key; *s != '\0'; ++s)
        {
            h = PR_ROTATE_LEFT32(h, 4) ^ *s;
        }
    
        return (h==0 ? ULONG_MAX : h);
    }
    
    int main(int argc, char ** argv)
    {
        printf("%d\n", ffgenhash(argv[1]));
        return 0;
    }
    

    如您所见,这里有两个生成相同缓存哈希键的真实 URL:

    ./ffgenhash "http://worldofsolitaire.com/decks/paris/5/12c.png"
    1087949033
    ./ffgenhash "http://worldofsolitaire.com/decks/paris/5/13s.png"
    1087949033
    

    由于我在 Javascript 循环中预加载了这些图像,因此无法在此处尝试使用某种空的

    确实,我认为我唯一真正的解决方案是以某种方式修改 Firefox 用户的 URL 以生成唯一的缓存哈希键。这就是我将使用的方法。

    顺便说一句,我很想创建一个 Firebug 添加,它会检查站点加载的所有资源,如果站点上的两个资源共享一个公共哈希键,那么开发人员就会知道,会给出一个很大的错误。通过这个来运行像谷歌地图这样的网站会很棒,因为在过去的几年里我看到这些图像有奇怪的东西:)

    【讨论】:

      【解决方案6】:

      这是 Sembiance 哈希生成器的修改版本,即使在 64 位平台上编译也能正常工作:

      #include <stdio.h>
      #include <string.h>
      #include <stdlib.h>
      
      #define ULONG_MAX 0xFFFFFFFF
      #define PR_ROTATE_LEFT32(a, bits) (((a) << (bits)) | ((a) >> (32 - (bits))))
      
      unsigned int ffgenhash(const char * key) {
          unsigned int h=0;
          for(const unsigned char * s = (unsigned char *) key; *s != '\0'; ++s) {
              h = PR_ROTATE_LEFT32(h, 4) ^ *s;
          }
          return (h==0 ? ULONG_MAX : h);
      }
      
      int main(int argc, char ** argv) {
          printf("%u\n", ffgenhash(argv[1]));
          return 0;
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-31
        • 1970-01-01
        • 2015-09-13
        • 1970-01-01
        • 1970-01-01
        • 2011-10-06
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多