7 Stimmen

Fehler im Algorithmus zur Erzeugung von Hash-Schlüsseln im Firefox-Cache

Es gibt ein Fehler in Firefox (auch in den neuen Betas und in Minenfeld-Releases), die das Zwischenspeichern bestimmter Dateien aufgrund des Algorithmus zur Erstellung eines Schlüssels in ihrem Cache-Hash verhindert. Hier ist ein Link zum Quellcode der Funktion .

Ich möchte sicherstellen, dass alle Dateien meiner Website im Cache gespeichert werden können. Ich verstehe jedoch nicht, warum ihre Hashing-Funktion keine eindeutigen Schlüssel für bestimmte URLs erzeugt. Ich hoffe, jemand kann dies beschreiben mal Funktion in Psuedo-Code oder Java.

Es wäre gut, ein Dienstprogramm für Entwickler zu erstellen, um eindeutige URLs sicherzustellen, bis dieser Fehler behoben ist.


EDIT : Es gab einige sehr hilfreiche Antworten, aber ich brauche mehr schrittweise Hilfe, um ein Dienstprogramm zu erstellen, das diese Cache-Verwechslungen aufspürt. Es wäre toll, einen Java-Code zu bekommen, der die von Firefox erzeugten Schlüssel reproduzieren kann. Daher die Eröffnung eines Kopfgeldes auf diese Frage.


EDIT 2: Hier ist eine teilweise funktionierende Java-Portierung (geschrieben mit Verarbeitung ). Beachten Sie die Tests am unteren Rand; die ersten drei funktionieren wie erwartet, die anderen jedoch nicht. Ich vermute, dass es etwas mit signierten/unsignierten Ints zu tun hat. Vorschläge?

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

}

6voto

i_am_jorf Punkte 52346

Soweit ich den Bugzilla-Eintrag gelesen habe, tritt der Fehler auf, wenn zwei verschiedene Probleme auftreten:

  1. Ihr Hash-Algorithmus erzeugt Kollisionen für Urls, die "ähnlich genug" sind. Aus dem Fehler "ähnlich genug" scheint zu bedeuten, dass alle 4 Zeichen (oder vielleicht 8) die URLs gleich sind, und
  2. Ihre Logik für den Umgang mit Hash-Kollisionen schlägt fehl, weil sie die vorherige URL mit demselben Hash-Wert noch nicht auf die Festplatte geschrieben haben.

Wenn Sie also eine Seite mit zwei sehr ähnlichen URLs haben, kann dies bei einigen Versionen von Firefox passieren. Ich würde erwarten, dass es im Allgemeinen nicht auf verschiedenen Seiten passiert, da FF dann Zeit hat, die Einträge auf die Festplatte zu spülen und das Zeitproblem zu vermeiden.

Wenn Sie also mehrere Ressourcen (Skripte, Bilder usw.) haben, die alle von derselben Seite geladen werden, stellen Sie sicher, dass sie eine Reihe von 9 Zeichen haben, die völlig unterschiedlich sind. Eine Möglichkeit, dies zu gewährleisten, ist das Anhängen eines Querystrings (den Sie ignorieren) mit einem zufälligen Bit an Daten, etwa wie:

5voto

FryGuy Punkte 8474

Der Algorithmus funktioniert folgendermaßen:

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

visuell (16-Bit-Version):

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

Das bedeutet, dass bei gleich langen und größtenteils gleichen Zeichenketten in mindestens einem Fall die unteren 4 Bits eines Zeichens und die oberen 4 Bits des nächsten Zeichens oder die jeweils anderen eindeutig sein müssen. Die Methode, die 32-Bit-Zahl in eine Tabelle zu kleben, könnte jedoch immer schwächer sein, d.h. sie erfordert, dass die unteren4 xoder oberen4 einer bestimmten Stelle in der Zeichenfolge (mod 8 Zeichen) eindeutig sind.

2voto

Sembiance Punkte 871

Dieser Fehler war ein großes Problem für meine Website: http://worldofsolitaire.com

Ich habe das Problem vor langer Zeit mit einer bedingten Regel in einer .htaccess-Datei umgangen, die ALLE Zwischenspeicherungen von Bildern auf der Website für Firefox-Benutzer deaktivieren würde. Das war eine schreckliche Sache, die ich tun musste, aber zu der Zeit konnte ich den Fehler in Firefox nicht ausfindig machen und es ist besser, die Seite etwas langsamer zu machen, als doppelte/beschädigte Bilder anzuzeigen.

Als ich in dem verlinkten Fehler las, dass er in den letzten Firefox-Versionen behoben wurde, änderte ich die Bedingung am 19. April 2009 (gestern), um die Zwischenspeicherung nur für Firefox 2-Nutzer zu deaktivieren.

Ein paar Stunden später habe ich über 10 E-Mails von Firefox 3-Benutzern erhalten (bestätigt), dass sie doppelte Bilder sehen. Also ist dieses Problem immer noch ein Problem in Firefox 3.

Ich beschloss, ein einfaches Linux-Testprogramm zu erstellen, mit dem ich prüfen kann, ob die URLs dieselben Cache-Hash-Schlüssel erzeugen.

Zum Kompilieren in einem Linux-System: g++ -o ffgenhash ffgenhash.cpp

Hier ist der Code (in der Datei ffgenhash.cpp speichern)

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

Wie Sie sehen können, sind hier zwei reale URLs, die denselben Cache-Hash-Schlüssel erzeugen:

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

Da ich diese Bilder in einer Javascript-Schleife vorlade, ist es hier nicht möglich, eine Art leeren <script>-Tag als Workaround zu verwenden.

Ich denke, meine einzige wirkliche Lösung besteht darin, die URLs für Firefox-Benutzer so zu ändern, dass ein eindeutiger Cache-Hash-Schlüssel erzeugt wird. Das ist also der Ansatz, den ich verwenden werde.

Übrigens bin ich schon fast versucht, eine Firebug-Erweiterung zu erstellen, die alle von einer Website geladenen Ressourcen überprüft und eine große Fehlermeldung ausgibt, wenn zwei Ressourcen auf der Website einen gemeinsamen Hash-Schlüssel haben, damit der Entwickler darauf aufmerksam wird. Es wäre toll, Seiten wie Google Maps damit zu überprüfen, da ich in den letzten Jahren seltsame Dinge mit diesen Bildern gesehen habe :)

1voto

jpalecek Punkte 45829

Erstens können nicht alle Zeichenketten eindeutig in Ganzzahlen gehasht werden (offensichtlich gibt es mehr Zeichenketten als Ganzzahlen (fester Größe), so dass es zu Kollisionen kommen muss). Sie können eine Hashtabelle haben, die alle Datensätze (z. B. alle Ihre Dateien) enthalten kann, aber um das zu erreichen, müssen Sie den Code der Hashtabelle ändern, nicht die Hashing-Funktion.

Zweitens sehe ich ein Problem mit der Hashing-Funktion, die Sie gepostet haben, in diesem Teil:

PR_ROTATE_LEFT32(h, 4)

Wenn es wirklich eine Rotation von h (ich habe das nicht überprüft), Rotation durch 4 bedeutet, dass Strings, bei denen zwei 8-Byte-Teile (ich nehme an 32-Bit-Hash) vertauscht sind (z. B.. xxxxxxxxyyyyyyyy vs. yyyyyyyyxxxxxxxx ) haben den gleichen Hash. Wenn Sie den Wert auf eine relative Primzahl zur Hash-Größe ändern (z. B. 5), wird dies nur für vertauschte Teile der Länge 32 geschehen.

1voto

Dies ist eine modifizierte Version des Hash-Generators von Sembiance, die auch dann korrekt funktioniert, wenn sie auf einer 64-Bit-Plattform kompiliert wird:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X