From 0e6d607484111135fa9d3227d409382947d10c7e Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 9 May 2024 21:25:10 -0400 Subject: [PATCH] Add checksum validation when loading trie buffers in selfie Related issue: https://github.com/uBlockOrigin/uBlock-issues/issues/3217#issuecomment-2103048654 --- src/js/biditrie.js | 27 ++++++++++++++++----------- src/js/hntrie.js | 32 +++++++++++++++++++------------- 2 files changed, 35 insertions(+), 24 deletions(-) diff --git a/src/js/biditrie.js b/src/js/biditrie.js index 132931638..06566d2e1 100644 --- a/src/js/biditrie.js +++ b/src/js/biditrie.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals WebAssembly, vAPI */ - -'use strict'; - /******************************************************************************* A BidiTrieContainer is mostly a large buffer in which distinct but related @@ -124,6 +120,15 @@ const BCELL_EXTRA_MAX = 0x00FFFFFF; const toSegmentInfo = (aL, l, r) => ((r - l) << 24) | (aL + l); const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); +// http://www.cse.yorku.ca/~oz/hash.html#djb2 +const i32Checksum = (buf32) => { + const n = buf32.length; + let hash = 177573 ^ n; + for ( let i = 0; i < n; i++ ) { + hash = (hash << 5) + hash ^ buf32[i]; + } + return hash; +}; class BidiTrieContainer { @@ -577,18 +582,18 @@ class BidiTrieContainer { } toSelfie() { - return this.buf32.subarray( - 0, - this.buf32[CHAR1_SLOT] + 3 >>> 2 - ); + const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + return { buf32, checksum: i32Checksum(buf32) }; } fromSelfie(selfie) { - if ( selfie instanceof Uint32Array === false ) { return false; } - let byteLength = selfie.length << 2; + if ( selfie instanceof Object === false ) { return false; } + if ( selfie.buf32 instanceof Uint32Array === false ) { return false; } + if ( selfie.checksum !== i32Checksum(selfie.buf32) ) { return false; } + const byteLength = selfie.buf32.length << 2; if ( byteLength === 0 ) { return false; } this.reallocateBuf(byteLength); - this.buf32.set(selfie); + this.buf32.set(selfie.buf32); return true; } diff --git a/src/js/hntrie.js b/src/js/hntrie.js index cc726db5d..4f89a9964 100644 --- a/src/js/hntrie.js +++ b/src/js/hntrie.js @@ -19,10 +19,6 @@ Home: https://github.com/gorhill/uBlock */ -/* globals WebAssembly */ - -'use strict'; - /******************************************************************************* The original prototype was to develop an idea I had about using jump indices @@ -115,7 +111,7 @@ */ const PAGE_SIZE = 65536; - // i32 / i8 +// i32 / i8 const TRIE0_SLOT = 256 >>> 2; // 64 / 256 const TRIE1_SLOT = TRIE0_SLOT + 1; // 65 / 260 const CHAR0_SLOT = TRIE0_SLOT + 2; // 66 / 264 @@ -124,6 +120,16 @@ const TRIE0_START = TRIE0_SLOT + 4 << 2; // 272 const roundToPageSize = v => (v + PAGE_SIZE-1) & ~(PAGE_SIZE-1); +// http://www.cse.yorku.ca/~oz/hash.html#djb2 +const i32Checksum = (buf32) => { + const n = buf32.length; + let hash = 177573 ^ n; + for ( let i = 0; i < n; i++ ) { + hash = (hash << 5) + hash ^ buf32[i]; + } + return hash; +}; + class HNTrieContainer { constructor() { @@ -446,16 +452,16 @@ class HNTrieContainer { } toSelfie() { - return this.buf32.subarray( - 0, - this.buf32[CHAR1_SLOT] + 3 >>> 2 - ); + const buf32 = this.buf32.subarray(0, this.buf32[CHAR1_SLOT] + 3 >>> 2); + return { buf32, checksum: i32Checksum(buf32) }; } fromSelfie(selfie) { - if ( selfie instanceof Uint32Array === false ) { return false; } + if ( selfie instanceof Object === false ) { return false; } + if ( selfie.buf32 instanceof Uint32Array === false ) { return false; } + if ( selfie.checksum !== i32Checksum(selfie.buf32) ) { return false; } this.needle = ''; - let byteLength = selfie.length << 2; + let byteLength = selfie.buf32.length << 2; if ( byteLength === 0 ) { return false; } byteLength = roundToPageSize(byteLength); if ( this.wasmMemory !== null ) { @@ -466,9 +472,9 @@ class HNTrieContainer { this.buf = new Uint8Array(this.wasmMemory.buffer); this.buf32 = new Uint32Array(this.buf.buffer); } - this.buf32.set(selfie); + this.buf32.set(selfie.buf32); } else { - this.buf32 = selfie; + this.buf32 = selfie.buf32; this.buf = new Uint8Array(this.buf32.buffer); } // https://github.com/uBlockOrigin/uBlock-issues/issues/2925