-
Notifications
You must be signed in to change notification settings - Fork 0
Description
Detail Bug Report
Summary
- Context:
StorageManagermanages multiple storage backends (cookies, localStorage, sessionStorage, memory) and provides a fallback mechanism in case a requested storage type is not available in the current environment. - Bug: The
getStoragemethod contains an infinite loop because the fallback logic uses the original, unchangingtypeparameter to find the index of the next storage to try. - Actual vs. expected: When a storage type is unavailable, the code should iterate through the
TYPESfallback chain until it finds an available one; instead, it repeatedly attempts to create the same fallback storage type (e.g., always tryinglocalStorageifcookieStorageis unavailable). - Impact: Severe. If both the requested storage and its immediate fallback are unavailable (common in certain privacy-restricted browser settings), the application will enter an infinite loop, hanging the process and eventually crashing with a JavaScript heap out of memory (OOM) error.
Code with Bug
getStorage(type: StorageType): IStorage {
if (!this.storages.has(type)) {
let storage = this.createStorage(type);
// If storage is not available, try next
while (!storage.isAvailable()) {
const index = TYPES.indexOf(type); // <-- BUG 🔴 type never changes, so index is always the same
logger.warn(
`Storage ${type} is not available, trying ${TYPES[index + 1]}`
);
storage = this.createStorage(TYPES[index + 1]); // <-- BUG 🔴 always retries the same fallback type
}
// Add to cache
this.storages.set(type, storage);
}
return this.storages.get(type)!;
}Explanation
The while loop is intended to advance through the TYPES fallback list until an available storage is found. However, it computes index from the original type parameter each iteration, and never updates type (or a separate “current type” variable). If the first fallback (TYPES[index + 1]) is also unavailable, the loop repeats forever, continually recreating and checking the same fallback storage.
Failing Test
it("should fall into an infinite loop when fallback storage is also unavailable", function () {
this.timeout(10000);
sinon.stub(CookieStorage.prototype, "isAvailable").returns(false);
sinon.stub(WebStorage.prototype, "isAvailable").returns(false);
// This hangs and crashes the process
storageManager.getStorage("cookieStorage");
});Test output:
FATAL ERROR: Ineffective mark-compacts near heap limit Allocation failed - JavaScript heap out of memory
Recommended Fix
Track the current storage type being attempted (e.g., currentType) and advance it on each fallback step; also guard against reaching the end of TYPES.
History
This bug was introduced in commit 72186ce. This commit refactored the storage management into a centralized StorageManager with a fallback mechanism, but the while loop logic used the original storage type to calculate the fallback index instead of updating it, causing an infinite loop when multiple storage types are unavailable.