Closed Bug 1555491 Opened 7 months ago Closed 5 months ago

Privacy Badgers users occasionally see error messages related to IndexedDB

Categories

(WebExtensions :: General, defect, P1)

67 Branch
defect

Tracking

(firefox70 verified, firefox71 verified)

VERIFIED FIXED
mozilla70
Tracking Status
firefox70 --- verified
firefox71 --- verified

People

(Reporter: daly, Assigned: rpl)

References

(Regressed 1 open bug)

Details

Attachments

(2 files)

User Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10.14; rv:67.0) Gecko/20100101 Firefox/67.0

Actual results:

Several bugs have been filed with Privacy Badger that display errors related to IndexedDB. The two separate error messages are:

AbortError: A request was aborted, for example through a call to IDBTransaction.abort

InvalidStateError: A mutation operation was attempted on a database that did not allow mutations.

Is there anything we or our users can do to prevent these error messages from occurring? Thanks.

Component: Untriaged → DOM: IndexedDB
Product: Firefox → Core

Link to the relevant issue in Privacy Badger: https://github.com/EFForg/privacybadger/issues/2156

Thanks for filing this bug!

As I understand what's happening:

  • PrivacyBadger reports errors observed on chrome.runtime.lastError.
  • IndexedDB errors are being reported here that don't seem to be caused by PrivacyBadger's own actions, suggesting WebExtensions internals like those introduced in bug 1406181 may be involved.
  • PrivacyBadger takes all errors seriously and so it's relaying the error to users, but they're not actionable for the user as posed. PB is going to filter them out in https://github.com/EFForg/privacybadger/pull/2378/files or elsewhere, but this bug was nicely filed so every WebExtension doesn't have to deal with this.

I think the root of the problem is that event bubbling happens for IDB errors and our IndexedDB wrapper at https://searchfox.org/mozilla-central/source/toolkit/modules/IndexedDB.jsm doesn't stop the bubbling from hitting the global's onerror handler.

:kmag, you added the IndexedDB wrapper in bug 1344590. What are your thoughts on how to avoid the undesired bubbling while making sure that whatever error handling is expected to exist still works (and tests don't break) etc? Feel free to steal this to a webextensions component if you like :)

Meta-wise:

  • There was an IndexedDB issue open about how far to propagate unhandled errors at https://github.com/w3c/IndexedDB/issues/49. That doesn't effect this.
  • We've changed some IndexedDB behavior to more aggressively/correctly clean up/shutdown in-flight IDB on global termination, so it's possible these errors were not being generated previously because they were firing after the JS global was totally dead.
Flags: needinfo?(kmaglione+bmo)
Priority: -- → P2

(In reply to Andrew Sutherland [:asuth] (he/him) from comment #2)

I think the root of the problem is that event bubbling happens for IDB errors and our IndexedDB wrapper at https://searchfox.org/mozilla-central/source/toolkit/modules/IndexedDB.jsm doesn't stop the bubbling from hitting the global's onerror handler.

:kmag, you added the IndexedDB wrapper in bug 1344590. What are your thoughts on how to avoid the undesired bubbling while making sure that whatever error handling is expected to exist still works (and tests don't break) etc? Feel free to steal this to a webextensions component if you like :)

Hm. That doesn't sound right. IndexedDB.jsm always uses the indexedDB object from the shared JSM global. There shouldn't actually be an event target for those errors to bubble up to, and even if there were, it shouldn't be one that any extension context should even come close to having access to...

Flags: needinfo?(kmaglione+bmo)

Okay, then the problem is likely a more general WebExtensions internal plumbing issue then if it's being exposed at chrome.runtime.lastError. Moving to WebExtensions.

Component: DOM: IndexedDB → General
Priority: P2 → --
Product: Core → WebExtensions
Assignee: nobody → lgreco
Priority: -- → P2

It looks like this is coming from a storage.local.set call, which really should not be propagating internal indexedDB errors...

I guess the fact that we create the indexed DB instance for an extension principal might mean that the errors it propagates are accessible by the extension caller scope?

Status: UNCONFIRMED → ASSIGNED
Ever confirmed: true
Assignee: lgreco → nobody
Status: ASSIGNED → NEW
Priority: P2 → P3
Assignee: nobody → lgreco
Status: NEW → ASSIGNED
Priority: P3 → P1
Attachment #9075197 - Attachment description: Bug 1555491 - Normalize errors raised from the storage.local IndexedDB backend. r?mixedpuppy!,asuth → Bug 1555491 - Normalize errors raised from the storage.local IndexedDB backend. r?kmag!,asuth
Depends on: 1563299
Pushed by luca.greco@alcacoop.it:
https://hg.mozilla.org/integration/autoland/rev/2c4c4dd5de14
Normalize errors raised from the storage.local IndexedDB backend. r=kmag,ttung
Status: ASSIGNED → RESOLVED
Closed: 5 months ago
Resolution: --- → FIXED
Target Milestone: --- → mozilla70
Regressions: 1564863
Regressions: 1564871

What does the following error mean? Is there anything extensions can do about it?

QuotaExceededError: The current transaction exceeded its quota limitations.

This is another error writing to extension storage that Privacy Badger surfaces to its users by default . It might be new to us. We just got a report about it on our GitHub: https://github.com/EFForg/privacybadger/issues/2417

See https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria for an overview.

You can use https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/estimate to introspect your usage and the quota limit your extension is being held to.

If the usage is very high, it's possible that your extension is the victim of an IndexedDB bug where large values stored to IndexedDB may be "spilled" to disk where they live as separate files outside the database proper that are subject to reference counting and the reference counting goes awry due to what we believe to be multiprocess shutdown complications. Investigation is ongoing on that bug.

In the interim, probably the most advisable thing one can do is delete the database and re-create it. If this is the database created on your behalf by the WebExtensions code, the WebExtensions code probably wants to get fancier.

If your extension is actually using a ton of data, use less. Or if the StorageEstimate.quota returned (https://developer.mozilla.org/en-US/docs/Web/API/StorageEstimate) indicates there's an unreasonably small amount of storage available to the extension, you'd need to tell the user they need to free up disk space. Again, this might be a thing that the WebExtensions code needs to do on your behalf.

While the IDB bug is Firefox-specific, the quota error is a cross-browser standard thing (https://heycam.github.io/webidl/#quotaexceedederror).

So just to be clear, is QuotaExceededError about disk usage?

Our user reports their Badger using 2.11 MB from running the following in Badger's background page. Is that an amount close to the quota limit?

chrome.storage.local.get(null, r=>console.log(
  parseFloat(JSON.stringify(r).length / 1024 / 1024).toFixed(2), "MB"));

(In reply to Alexei from comment #12)

So just to be clear, is QuotaExceededError about disk usage?

Yes, the exception is thrown when a requested write would exceed quota.

No, 2 megabytes shouldn't be a problem unless the user's disk is basically full.

The following code snippet if run from the browser console (ctrl-shift-j on linux, accessible from the firefox menu thing via "Web Developer" followed by locating "Browser Console") will produce helpful output:

code snippet:

Services.qms.getUsage((request) => { for (let usage of request.result) { if (usage.origin === 'chrome') continue; let prin = Services.scriptSecurityManager.createContentPrincipalFromOrigin(usage.origin); if (prin.addonPolicy && prin.addonPolicy.debugName.includes('Privacy Badger')) { console.log('addon', prin.addonPolicy.debugName, 'origin', usage.origin, 'uses', usage.usage); }}}, true);

produces output:

addon "Privacy Badger" (ID: jid1-MnnxcxisBPnSXQ@jetpack, moz-extension://6029dafd-7d29-4d29-bc60-f4d0e6560ede/) origin moz-extension://6029dafd-7d29-4d29-bc60-f4d0e6560ede^userContextId=4294967295 uses 122880 debugger eval code:1:294
addon "Privacy Badger" (ID: jid1-MnnxcxisBPnSXQ@jetpack, moz-extension://6029dafd-7d29-4d29-bc60-f4d0e6560ede/) origin moz-extension://6029dafd-7d29-4d29-bc60-f4d0e6560ede uses 65536

Can you please provide some steps to manually verify this issue? If no manual testing is needed please mark this bug as "qe-verify-"

Flags: needinfo?(lgreco)

(In reply to Madalin Cotetiu from comment #15)

Can you please provide some steps to manually verify this issue? If no manual testing is needed please mark this bug as "qe-verify-"

Manually triggering this error on PrivacyBadger may be a bit tricky, as it seems that PrivacyBadger gradually store more data while the user navigates on more websites and it detects more trackers, and I couldn't find a simple way to make it immediately store as much data as needed to exceed the quota enforced by the QuotaManagerService.

Locally I've been able to trigger manually these kind of errors (QuotaManagerExceeded, AbortError, InvalidStateError and/or UnknownError) by forcing a disk quota at a file system level, using a test extension that writes (a considerable amount of) data into storage.local and filling the available disk space by creating more files into the same partition.

Another approach (that doesn't need a disk quota to be set at the file system level) could be to run Firefox on a profile stored in a small disk partition (e.g. using a small usb key could likely work as well).

The QuotaManagerExceeded is a bit simpler to trigger, because it is triggered consistently if the extension exceeded its quota (based on the free disk space currently still available).

From what I saw AbortError / InvalidStateError / UnknownError are a bit trickier to trigger consistently, but I have been able to trigger them manually by completely filling the disk space available on the partition where the test Firefox profile is and then trying to call browser.storage.local.set to store more data.

Obviosly, filling the entire disk space available to the test Firefox profile may also trigger many other unrelated failures (e.g. Firefox will not be able to update many of the other state and data files that are part of the Firefox profile), and so some of the operations (e.g. installing the extension to test) has to be done before there is no disk space anymore.

Let's give a try to the following STR:

  • Run Firefox from a profile stored in a disk with a short amount of disk space (I used a disk limited to 100Mb and then gradually filled the disk space. I used some shell commands but it could also be done by manually copying other files into that disk partition using the OS file manager)
  • install the attached test extension
  • open the addon debugging window for the test extension from about:debugging and select the console panel
  • press the test extension browserAction button (which will store some data in storage.local)

For QuotaManagerExceeded (with some disk space still available in the disk partition where the test Firefox profile is stored):

  • the error message "QuotaManagerExceeded: storage.local API call exceeded its quota limitations." should have been logged from the test extension "background.js" script

For AbortError / InvalidStateError / UnknownError (with no disk space available in the disk partition where the test Firefox profile is stored):

  • the error message "An unexpected error occurred" should have been logged from the test extension "background.js" script
  • the original error (AbortError / InvalidStateError / UnknownError) should have been also logged from ExtensionStorageIDB.jsm

On PrivacyBadger, I've been able to trigger this last STR at least once, by filling the entire disk space (after installing PrivacyBadger) and then opening a number of website until PrivacyBadger shows a red badge (and an exclamation mark: "!") on its browserAction icon, at that point the "An unexpected error occurred" error message has been shown in the PrivacyBadger browserAction popup.

Flags: needinfo?(lgreco)

Regarding "An unexpected error occurred" errors: We at Privacy Badger should ignore these, right? Since there isn't anything Privacy Badger or the user can do about them.

(In reply to Alexei from comment #17)

Regarding "An unexpected error occurred" errors: We at Privacy Badger should ignore these, right? Since there isn't anything Privacy Badger or the user can do about them.

"An unexpected error occurred" is basically a generic error message that is going to be raised for errors coming from IndexedDB and not clearly related to something that the extension itself has triggered and can handle (like DataCloneError, when some of the data being written cannot be serialized, and QuotaExceededError, when the extension is trying to write on disk more data than allowed by the quota).

As I briefly described in Comment 16, AbortError / InvalidStateError / UnknownError errors seem to also be triggered (instead of QuotaExceededError) when the disk is completely full (or it becomes completely full while IndexedDB is already processing the IndexedDB request), but that would usually mean that something external to the extension is using all the available disk space and there is likely nothing that the extension can do to handle it.

It may be worth for the extension to let the user know that it may be related to a disk full issue, so that the user is aware and may check that the disk in not full (Firefox should actually also show a yellow "warning notification bar" when it detects that the disk is almost full).

(In reply to Andrew Sutherland [:asuth] (he/him) from comment #14)

Yes, the exception is thrown when a requested write would exceed quota.

No, 2 megabytes shouldn't be a problem unless the user's disk is basically full.

I'm getting this error from privacy badger right now. I did recently have my disk fill up, but currently have 7gb free. It seems like this error should have gone away some time after I freed up this disk space (about a day ago)?

Privacy Badger currently keeps showing the error until you restart it or the browser (which restarts Privacy Badger of course).

So, it is expected that you would need to restart Firefox before it updates its concept of how much free disk space, yes. There are ways to improve this, but there are also privacy and fingerprinting concerns that the current approach helps mitigate the worst of, so it's not likely to change in the immediate future. (If site quota was a realtime data stream that could be sampled, separate sites/origins could communicate through this channel and/or infer the operation of other non-browser software on your computer, etc.)

See Also: → 1282972

Verified fix on Windows 10 64-bit and Ubuntu 18.04.3 LTS with latest Ff Beta 70.0b7 (20190916074538), Ff 71.0a1 (20190917155453).
On Beta when there is still very little disk space available, in the test extension console, after pressing the extension browserAction button, the error message "QuotaManagerExceeded: storage.local API call exceeded its quota limitations." has been logged. Also it is shown in the Privacy Badger console.
When no disk space is available, one of the expected error messages “AbortError” is logged from ExtensionStorageIBD.jsm and the “An unexpected error occurred” is logged from the “background.js” script, but this I could only reproduce on Beta, not on Nightly.
Tested this with another profile which was created on a larger space no error messages are received in either text extension or Privacy Badger console.

Status: RESOLVED → VERIFIED

What is the error raised when an extension exceeds the ~5 MB extension storage quota? Is it "QUOTA_BYTES quota exceeded", same as in Chrome?

At this point I understand QuotaExceededError to be the Firefox version of Chrome's FILE_ERROR_NO_SPACE: an error that most likely has nothing to do with your extension. Sorry if I misunderstood. We are trying to figure out which storage errors Privacy Badger should care about, and which it should ignore.

QuotaExceededError is the web platform standard for reporting insufficient quota/space errors (https://heycam.github.io/webidl/#dom-domexception-quota_exceeded_err). IndexedDB has a slightly more useful description at https://w3c.github.io/IndexedDB/#exceptions, but the general idea is that content is not supposed to be able to distinguish between hitting a quota limit and the device's storage being filled up because that would aid in fingerprinting and cross-origin information leaks, hence the conflation.

For the purposes of a WebExtension that uses a relatively constant amount of space, it's probably fine to assume that QuotaExceededError or Chrome's internal file error of FILE_ERROR_NO_SPACE are due to system issues.

And to be clear, the only 5 megabyte storage limit I'm aware of is for the LocalStorage API. Firefox's current quota limit heuristics are described at https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API/Browser_storage_limits_and_eviction_criteria#Storage_limits and should be between 10% of free disk space and 2 GiB.

Ah OK, thank you for clarifying. If I follow you correctly, it sounds like there is no extension storage (chrome.storage.local)-specific QUOTA_BYTES-type error (https://developer.chrome.com/apps/storage#property-local-QUOTA_BYTES) in Firefox.

That's a bit unexpected given that Firefox does support the "unlimitedStorage" manifest permission. According to https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/manifest.json/permissions#Unlimited_storage, unlimitedStorage "enables extensions to exceed any quota imposed by the storage.local API". It sounds like there is no storage.local quota at this time.

Yes, that's correct. Right now unlimitedStorage translates to the equivalent of having navigator.storage.persist() (https://developer.mozilla.org/en-US/docs/Web/API/StorageManager/persist) granted for your origin. It exempts the extension from storage limits and eviction. However, if the disk fills up, you'll still get QuotaExceededError (or an AbortError that really should be a QuotaExceededError).

Note that I'm mainly discussing things from the browser storage APIs, but that's what IndexedDB is and the storage.local API uses under the hood.

You need to log in before you can comment on or make changes to this bug.