2009-03-20 3 views
7

キャッシュハッシュにキーを作成するアルゴリズムのために、特定のファイルのキャッシュを防止するa bug in Firefox(新しいベータ版と地雷のリリースでさえも)があります。 Here is a link to the source code of the functionfirefoxキャッシュハッシュキー生成アルゴリズムのバグ

サイトのすべてのファイルをキャッシュできるようにしたいと思います。しかし、私はなぜ彼らのハッシュ関数が別のURLのためのユニークなキーを作成することに失敗したのか分かりません。私は誰かがこのの機能を擬似コードまたはjavaで記述できると考えています。

このバグが修正されるまで、開発者が一意のURLを保証するためのユーティリティを作成するとよいでしょう。


EDIT:しかし、私はこれらのキャッシュmixupsをチェックするためのユーティリティを作成するために、より多くのステップバイステップの助けを必要とし、いくつかの非常に有用な答えがありました。 Firefoxが作成しているキーを再現できるJavaコードを取得することは素晴らしいことです。したがって、この疑問に恩恵をもたらす。


EDIT 2:ここではは(processingを使用して書かれた)部分的に取り組んでJavaのポートです。一番下のテストに注意してください。最初の3つは期待通りに動作しますが、それ以外は動作しません。私は、署名された/署名のないintsに関する何かを疑う。提案?

// 
// 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 

} 
+0

正に、私はあなたがこれについて全部あまりにも心配していると思います。あなたは何らかの問題を経験しているのですか、それともこのすべての早すぎる最適化ですか? –

+0

に問題があります。 :/ – jedierikb

+0

問題の詳細な説明:何千ものファイルが正しくキャッシュされるようにするための戦略を立てる必要があります。今、彼らはそうではありません。すべてのファイル名を事前処理してキャッシュ可能であることを確認します。 – jedierikb

答えて

5
ここ

アルゴリズムがどのように働くかある:視覚

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 

これが意味することであるあなたは、同じ長さの文字列を持っていると、ほぼ同じであるならば、少なくとも一つの場合には、チャーの下位4ビットの上位4ビット次のcharまたはxorは一意でなければなりません。しかし、32ビット数をテーブルに貼り付ける方法は、弱くなる可能性があります。つまり、文字列内の特定の場所のlower4 xor upper4(mod 8 chars)が一意である必要があります。

6

二つの別個の問題が発生したときにバグが現れ、私はBugzillaのエントリを読んで理解して何から:

  1. 彼らのハッシュアルゴリズムは、「十分に類似」しているURLの衝突を発生させます。バグ「類似している」は、4文字(または8文字)のすべてが同じであることを意味するように思われます。
  2. ハッシュ衝突を処理するロジックは、同じハッシュ値で前のURLをフラッシュしなかったため失敗しますまだディスクに。

したがって、2つの非常に似たURLを持つページがある場合、これはFirefoxの一部のバージョンで発生する可能性があります。それは一般的には異なるページで発生することはありませんが、私は期待しています.FFFにはタイミング問題を避けるためにディスクへのエントリをフラッシュする時間があるからです。

したがって、同じページからすべて読み込まれた複数のリソース(スクリプト、画像など)がある場合は、完全に異なる9文字の実行があることを確認してください。

+0

ええ、私はバイトであったはずのバイトを読み込み、それを文字に精神的に変換しました。以下の他のものは、ハッシュアルゴリズムについての良い説明を持っています。 –

+0

クエリ文字列の提案は良いですが、前処理として自分のファイルの一意のURLを確保したいと考えています。 – jedierikb

+0

また、実行時にランダムなクエリ文字列を追加するには、そのランダムなクエリ文字列をどこかにキャッシュし、衝突しないパターンを開発する必要があります。 – jedierikb

1

:?あなたがこれを確実かもしれない一つの方法は、データのランダムビットを(あなたが無視すること)のようなものをクエリ文字列を追加することですすべての文字列を一意にハッシュすることはできません(明らかに、(固定サイズの整数)よりも多くの文字列が存在するため、衝突が必要で​​す)。すべてのデータセット(たとえばすべてのファイル)を保持できるハッシュテーブルを作成できますが、ハッシュテーブルのコードを変更する必要があります。ハッシュ関数は変更しないでください。

第二に、私はこの部分では、あなたが投稿ハッシュ関数の問題を参照してください。

PR_ROTATE_LEFT32(h, 4) 

それは本当にhの回転を行う場合(私はこれをチェックしていない)、その4によって回転します2つの8バイト(私は32ビットハッシュと仮定します)の部分をスワップした文字列(例えば、xxxxxxxxyyyyyyyyyyyyyyyyxxxxxxxx)は等しいハッシュを持ちます。あなたはハッシュサイズ(例えば5)、これが唯一の長さの入れ替え部品のため起こるのだろうと互いに素なものに変更した場合は32

+0

私は彼が求めている質問は、「どのように私はこの貧弱なハッシュ関数を回避することができますか」ではなく、「どうすればよりよいハッシュ関数を構築できるのか」だと思っています – FryGuy

0

あなたは本当に本当のバグについて誤解されているようです。確かに、ハッシュアルゴリズムの増加が間違った選択のためにハッシュの衝突があります。しかし、hash(x)= 1でさえ、説明された問題は発生しません。これは、O(1)ルックアップを最初のバケットを介したO(N)リンクリスト検索に変えるだけです。

本当の問題は、Firefoxがハッシュの衝突を処理できないことです。したがって、すべてのURLの完全なハッシュが必要です。 「すべてのURL」は残念ながら、あなたのコントロールの外にあるセットです。

+0

私のサイトのサブセット「すべてのURL」は、私のサイトの前処理ユーティリティと衝突します。 – jedierikb

2

このバグは私のサイトのための主要な問題でした:http://worldofsolitaire.com

私はFirefoxユーザーのためのサイト上の画像のすべてのキャッシュを無効にします.htaccessファイルでの条件付きのルールを使用して、長い時間前にそれを回避働きました。これは恐ろしいことでしたが、Firefoxの中でこのバグを追跡できず、サイトがわずかに遅くなると、重複した画像を表示するよりも優れています。

最新のFirefoxリリースで修正されたリンクバグを読んだとき、2009年4月19日(昨日)に条件を変更してFirefox 2ユーザーのキャッシュを無効にしました。

数時間後、私はFirefox 3ユーザーから(確認済みの)重複した画像を見ている電子メールを10通以上受信しました。だから、この問題はFirefox 3の問題です。

私は、同じキャッシュハッシュキーを生成しているかどうかを確認するための簡単なLinuxテストプログラムを作成することにしました。任意の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; 
} 

あなたがここに、見ることができるように、2つの現実の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の追加を作成すると、サイトでロードされたすべてのリソースをチェックし、サイトの2つのリソースが共通のハッシュキーを共有しているため、大きなエラーが発生します。ここ数年の間に私は奇妙なことを見てきたので、これを使ってGoogleマップのようなサイトを動かすのはすばらしいでしょう:)

1

これは64ビット版でコンパイルされた場合でも正しく動作するSembianceのハッシュジェネレータの修正版です。ビットプラットフォーム:

#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; 
}