From c098eb8625cde387ae13a5dfb623be6928a508ce Mon Sep 17 00:00:00 2001 From: Raymond Hill Date: Thu, 2 May 2024 20:21:47 -0400 Subject: [PATCH] Fix potential corruption when reading serialized data Corrpution would occur when reading back serialized data which contained multiple references to same instance of an object. The issue could manifest when reading cache storage-related data from the browser storage API, since the serializer is not used when reading from indexedDB. Private/incognito mode fall back on using browser storage API as cache storage. Off the top of my head, I think the following conditions all together could result in high likelihood of malfunction caused by improperly deserializing data at launch time: - Load from a selfie - Selfie created after uBO ran for a while - Selfie loaded from browser storage API (not the case by default) Possibly related to reports of uBO malfunctioning: https://github.com/uBlockOrigin/uBlock-issues/issues/3217#event-12686416838 --- src/js/s14e-serializer.js | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/src/js/s14e-serializer.js b/src/js/s14e-serializer.js index dda2fe47c2f44..0c9200ebcaa14 100644 --- a/src/js/s14e-serializer.js +++ b/src/js/s14e-serializer.js @@ -747,6 +747,8 @@ const _deserialize = ( ) => { } case I_OBJECT_SMALL: case I_OBJECT_LARGE: { + const out = {}; + readRefs.set(refCounter++, out); const entries = []; const size = type === I_OBJECT_SMALL ? charCodeToInt[readStr.charCodeAt(readPtr++)] @@ -756,48 +758,45 @@ const _deserialize = ( ) => { const v = _deserialize(); entries.push([ k, v ]); } - const out = Object.fromEntries(entries); - readRefs.set(refCounter++, out); + Object.assign(out, Object.fromEntries(entries)); return out; } case I_ARRAY_SMALL: case I_ARRAY_LARGE: { const out = []; + readRefs.set(refCounter++, out); const size = type === I_ARRAY_SMALL ? charCodeToInt[readStr.charCodeAt(readPtr++)] : deserializeLargeUint(); for ( let i = 0; i < size; i++ ) { out.push(_deserialize()); } - readRefs.set(refCounter++, out); return out; } case I_SET_SMALL: case I_SET_LARGE: { - const entries = []; + const out = new Set(); + readRefs.set(refCounter++, out); const size = type === I_SET_SMALL ? charCodeToInt[readStr.charCodeAt(readPtr++)] : deserializeLargeUint(); for ( let i = 0; i < size; i++ ) { - entries.push(_deserialize()); + out.add(_deserialize()); } - const out = new Set(entries); - readRefs.set(refCounter++, out); return out; } case I_MAP_SMALL: case I_MAP_LARGE: { - const entries = []; + const out = new Map(); + readRefs.set(refCounter++, out); const size = type === I_MAP_SMALL ? charCodeToInt[readStr.charCodeAt(readPtr++)] : deserializeLargeUint(); for ( let i = 0; i < size; i++ ) { const k = _deserialize(); const v = _deserialize(); - entries.push([ k, v ]); + out.set(k, v); } - const out = new Map(entries); - readRefs.set(refCounter++, out); return out; } case I_ARRAYBUFFER: {