There are many applications which interact with another application, which means they run their code as a DLL in a different process. This technique is used, for example, when an antivirus software tries to monitor/block navigation to a malicious website, or a screen reader tries to access UI parts. If such an application injects their code into Firefox, and if there is a bug in their code running in our firefox.exe, it will emerge as Firefox’s bug even though it’s not.
Firefox for Windows has a feature to prevent DLLs from being loaded into our processes. If we are aware that a particular DLL causes a problem in our processes such as a crash or performance degradation, we can stop the problem by blocking the DLL from being loaded.
This blocklist is about a third-party application which runs outside Firefox but interacts with Firefox. For add-ons, there is a different process.
This page explains how to request to block a DLL which you think we should block it as well as technical details about the feature.
- 1 But wait, should we really block it?
- 2 How to request to block a DLL
- 3 How to edit WindowsDllBlocklistDefs.in
- 4 How the blocklist blocks a DLL
- 5 Cases where we should not block a module
- 6 Third-party-module ping
- 7 Contact
But wait, should we really block it?
Blocking a DLL should be our last resort to fix a problem because doing it normally breaks functionality of an application which installed the DLL. If there is another option, we should always go for it. Sometimes we can safely bypass a third-party’s problem by changing our code even though its root cause is not on our side.
When we decide to block it, we must be certain that the issue at hand is so great that it outweighs the user's choice to install the software, the utility it provides, and the vendor's freedom to distribute and control their software.
How to request to block a DLL
Our codebase has the file named WindowsDllBlocklistDefs.in from which our build process generates DLL blocklists as C++ header files and compiles them. To block a new DLL, you create a patch to update WindowsDllBlocklistDefs.in and land it on our codebase, following our standard development process. Moreover, you need to fill out a form specific to the DLL blockling request so that reviewers can review the impact and risk as well as the patch itself.
Here are the steps:
- File a bug if it does not exist.
- Answer all the questions in this questionnaire, and attach it to the bug as a plaintext.
- Make a patch and start a code review via Phabricator as usual.
How to edit WindowsDllBlocklistDefs.in
WindowsDllBlocklistDefs.in defines several variables as a Python Array. When you add a new entry in the blocklists, you pick one of the variables and add an entry in the following syntax
Variable += [ ... # One-liner comment including a bug number EntryType(Name, Version, Flags), ... ]
ALL_PROCESSES | BROWSER_PROCESS | CHILD_PROCESSES | SOCKET_PROCESSES | UTILITY_PROCESSES
DllBlocklistEntry | A11yBlocklistEntry | RedirectToNoOpEntryPoint
|Name||A string representing a DLL's filename to block|
|Version|| One of the following formats:
BLOCK_WIN8_AND_OLDER | BLOCK_WIN7_AND_OLDER
Choose one of the following predefined variables.
DLLs defined here are blocked in BROWSER_PROCESS + CHILD_PROCESSES
DLLs defined here are blocked in the browser process
DLLs defined here are blocked in non-browser processes
DLLs defined here are blocked in socket processes
DLLs defined here are blocked in utility processes
Choose one of the following predefined EntryTypes.
Use this EntryType unless your case matches the other EntryTypes.
If you want to block a module only when it’s loaded by an accessibility application such as a screen reader, you can use this EntryType.
If a modules is injected via Import Directory Table, adding the module as DllBlocklistEntry breaks process launch, meaning DllBlocklistEntry is not an option. You can use RedirectToNoOpEntryPoint instead.
A string representing a DLL's filename to block. Don’t include a directory name.
A maximum version to be blocked. If you specify a value, a module with the specified version, older versions, and a module with no version are blocked.
If you want to block a module regardless of its version, use ALL_VERSIONS.
If you want to block a module with no version, use UNVERSIONED.
To specify a version, you can use either of the following formats:
- A tuple consisting of four digits. This is compared to the version that is embedded in a DLL as a version resource.
Example: (1, 2, 3, 4)
- A 32-bit integer representing a Unix timestamp with PETimeStamp. This is compared to an integer of IMAGE_FILE_HEADER::TimeDateStamp.
If you know a problem happens only on older Windows versions, you can use one of the following flags to narrow down the affected platform.
How the blocklist blocks a DLL
Briefly speaking, we make ntdll!NtMapViewOfSection return STATUS_ACCESS_DENIED if a given module is on the blocklist, thereby a third-party’s code, or even Firefox’s legitimate code, which tries to load a DLL in our processes in any way such as LoadLibrary API fails and receives an access-denied error.
Cases where we should not block a module
As our blocklist works as explained above, there are the cases where we should not block a module.
- A module is loaded via Import Directory Table
Blocking this type of module blocks even a process from launching. You may be able to block this type of module with RedirectToNoOpEntryPoint.
- A module is loaded as a Layered Service Provider
Blocking this type of module on Windows 8 or newer breaks networking. Blocking a LSP on Windows 7 is ok.
- A module is loaded via a Window hook
Blocking this type of module causes repetitive attempts to load a module, resulting in slow performance like Bug 1633718.
We’re collecting the third-party-module ping which captures a moment when a third-party module is loaded into the Browser/Tab/RDD process. As it’s asked in the request form, it’s important to check the third-party-module ping and see whether a module we want to block appears in the ping or not. If it appears, you may be able to know how a module is loaded by looking at a callstack in the ping.
How to view callstacks in the ping
- You can run a query on BigQuery console or STMO.
(BigQuery console is much faster and can handle larger data.)
- BigQuery console (visit here to request access): https://console.cloud.google.com/bigquery
- STMO: https://sql.telemetry.mozilla.org/
- Make your own query based on this template.
- Run the query.
- Save the result as a JSON file.
- In BigQuery console, click [SAVE RESULTS] and choose [JSON (local file)].
- In STMO, click [...] at the right-top corner and select [Show API Key], then you can download a JSON from a URL shown in the [Results in JSON format].
- Go to https://msmania.github.io/assets/mozilla/third-party-modules/
(A temporal link. Need to find a permanent place.)
- Click [Upload JSON] and select the file you saved at the step 4.
- Click a row in the table to view a callstack
How to see the versions of a specific module in the ping
You can use this template to query which versions of a specific module are captured in the ping. This tells the product versions which are actively used including the crashing versions and the working versions.
You can also get the crashing versions by querying the crash reports or the Socorro table. Having two version lists, you can decide whether you can specify the Version parameter in a blocklist entry.
Any question or feedback is welcome.