2009-03-20 3 views
7

캐시 해시에 키를 생성하기위한 알고리즘으로 인해 특정 파일의 캐싱을 방지하는 a bug in Firefox (새 베타 및 지뢰밭 릴리스 포함)이 있습니다. Here is a link to the source code of the function.firefox 캐시 해시 키 생성 알고리즘 버그

내 사이트의 파일을 모두 캐시 할 수있게하려는 것입니다. 그러나, 왜 자신의 해시 함수가 고유 한 URL에 대한 고유 키를 만들지 못하는지 이해할 수 없습니다. 누군가 이 psuedo-code 또는 java로 표시 될 수 있기를 바랍니다.

개발자가이 버그가 해결 될 때까지 고유 URL을 유지할 수있는 유틸리티를 만드는 것이 좋습니다.


편집 : 그러나, 나는이 캐시 혼입를 확인하기 위해 유틸리티를 만들 수있는 더 단계별로 도움을 필요로, 매우 도움이 답변이왔다. 파이어 폭스가 만들고있는 키를 재현 할 수있는 자바 코드를 얻는 것이 좋다. 그러므로,이 질문에 현상금을 여는 것.


EDIT 2 여기 (processing로 작성된) 부분적으로 작동하는 자바 포트이다. 하단의 테스트에 유의하십시오. 처음 세 개는 예상대로 작동하지만 다른 개는 작동하지 않습니다. 나는 서명/서명되지 않은 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 비트 서로의 다음 xor 또는 xor는 고유해야합니다. 그러나 32 비트 숫자를 테이블에 고정하는 방법은 약할 수 있습니다. 즉 문자열 (mod 8 문자)의 특정 위치의 lower4 xor 또는 upper4가 고유해야합니다.

6

는 버그는 두 가지 문제가 발생했을 때 명단 :

  1. 그들의 해시 알고리즘은 "충분히 유사한"있는 URL에 대한 충돌을 생성합니다. "similiar enough"은 4 문자 (또는 8 자)의 URL을 모두 의미하는 것으로 보입니다.
  2. 해시 충돌을 처리하는 논리는 이전 URL을 동일한 해시 값으로 플러시하지 않았기 때문에 실패합니다 아직 디스크에.

기본적으로 두 개의 매우 유사한 URL이있는 페이지가있는 경우 이는 일부 Firefox 버전에서 발생할 수 있습니다. 그것은 일반적으로 다른 페이지에서 발생하지 않을 것으로 예상됩니다. FF는 타이밍 문제를 피하면서 디스크로 항목을 플러시 할 시간을 가질 것이기 때문입니다.

따라서 동일한 페이지에서 모두로드되는 여러 리소스 (스크립트, 이미지 등)가있는 경우 완전히 다른 9 개의 문자가 실행되는지 확인하십시오. ?

+0

예, 비트가되어야하고 문자로 정신적으로 변환해야하는 바이트를 읽습니다. 아래의 다른 사람들은 해싱 알고리즘에 대해 잘 설명합니다. –

+0

쿼리 문자열 제안은 좋지만 사전 프로세스로 내 파일의 고유 URL을 보장하고자합니다. – jedierikb

+0

또한 런타임에 임의의 쿼리 문자열을 추가하려면 임의의 쿼리 문자열을 어딘가에 캐싱하고 충돌하지 않는 패턴을 개발해야합니다. – jedierikb

1

첫째 : 당신이 이것을 보장 할 수있는 한 가지 방법은 데이터의 임의의 비트, 같은과 함께 쿼리 문자열을 (당신이 무시 것을)를 추가하는 것입니다 , 모든 문자열을 정수로 해시 할 수는 없습니다 (분명히 (고정 크기) 정수보다 더 많은 문자열이 있으므로 충돌이 있어야합니다). 모든 데이터 세트 (예 : 모든 파일)를 보유 할 수있는 해시 테이블을 만들 수 있지만 가져 오려면 해시 함수가 아니라 해시 테이블의 코드를 변경해야합니다.

둘째, 나는이 부분에서, 당신이 게시 된 해시 기능에 문제를 참조하십시오

PR_ROTATE_LEFT32(h, 4) 

정말 4 명에 의해 회전, h의 회전 (나는이에 확인하지 않은)를 수행하는 경우 그 두 개의 8 바이트 (32 비트 해시 가정) 파트가 스왑 된 문자열 (예 : xxxxxxxxyyyyyyyyyyyyyyyyxxxxxxxx)은 동일한 해시를 갖습니다. 당신이 해시 크기 (. 예 : 5), 이것은 단지 길이의 교체 부품 일어날 서로 소인 뭔가를 변경하는 경우 (32)

+0

그가 물어 보는 질문은 '내가 어떻게이 해로운 해쉬 함수를 해결할 수 있을까?'가 아니라 '어떻게하면 더 나은 해시 함수를 만들 수 있을까'라고 생각한다. – FryGuy

0

당신은 분명히 진짜 버그에 대해 오해하고 있습니다. 물론 해시 알고리즘의 선택이 증가하기 때문에 해시 충돌이 발생합니다. 그러나 hash (x) = 1조차도 설명 된 문제가 발생하지 않습니다. 첫 번째 버킷을 통해 O (N) 연결된 목록 검색으로 O (1) 룩업을 돌릴뿐입니다.

실제 문제는 Firefox가 해시 충돌을 처리하지 못한다는 것입니다. 따라서 모든 URL의 완벽한 해시가 필요합니다. "모든 URL"은 불행하게도 사용자가 제어 할 수없는 부분입니다.

+0

적어도 내 사이트의 하위 집합 인 "모든 URL"이 내 사이트의 사전 처리 유틸리티와 충돌합니다. – jedierikb

2

이 버그는 내 사이트에 대한 중요한 문제였다 http://worldofsolitaire.com

나는 파이어 폭스 사용자를위한 사이트의 모든 이미지 캐싱을 사용하지 않도록 것 인 htaccess로 파일에 조건부 규칙을 사용하여 오래 전에 주위 일 . 이것은해야 할 끔찍한 일 이었지만 당시 Firefox에서 버그를 추적 할 수 없었고 사이트가 약간 느린 경우 중복/손상된 이미지를 보여주는 것보다 낫습니다.

최신 Firefox 릴리스에서 수정 된 링크 된 버그를 읽었을 때 2009 년 4 월 19 일 (어제) 조건부를 Firefox 2 사용자 전용 캐싱을 사용 중지하도록 변경했습니다.

몇 시간 후 Firefox 3 사용자 (확인)에서 10 장이 넘는 전자 메일을 받았는데 중복 된 이미지를보고있었습니다. 따라서이 문제는 Firefox 3에서 여전히 문제입니다.

동일한 캐시 해시 키를 생성하는지 확인하기 위해 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 

이후 미리로드 사용하려고, 자바 스크립트 루프에서 이러한 이미지를 어떤 종류의 비어있는 < 스크립트 > 태그 해결 방법은 여기에서 불가능합니다.

실제로 유일한 해결책은 고유 한 캐시 해시 키를 생성하기 위해 Firefox 사용자를 위해 URL을 수정하는 것입니다. 이것이 제가 사용하게 될 접근법입니다.

덧붙여서, 나는 사이트에서로드 된 모든 리소스를 검사하고 사이트의 두 리소스가 공통 해시 키를 공유하여 개발자가 알고있는 경우 큰 오류가 발생하는 Firebug를 추가하는 것이 유혹입니다. 지난 몇 년 동안 이러한 이미지로 이상한 것들을 보았 기 때문에 이것을 통해 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; 
}