Blocklisting/DLL: Difference between revisions

(fix link to WindowsDllBlocklistDefs.in)
(remove content and add link to firefox-source-docs page)
 
(One intermediate revision by the same user not shown)
Line 1: Line 1:
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.
This content has moved to https://firefox-source-docs.mozilla.org/widget/windows/blocklist.html
 
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 [https://extensionworkshop.com/documentation/publish/add-ons-blocking-process/ 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.
 
== 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 [https://searchfox.org/mozilla-central/source/toolkit/xre/dllservices/mozglue/WindowsDllBlocklistDefs.in 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 [https://bugzilla.mozilla.org/enter_bug.cgi?format=__default__&bug_type=defect&product=Toolkit&component=Blocklist%20Policy%20Requests&op_sys=Windows&short_desc=DLL%20block%20request%3A%20%3CDLL%20name%3E&comment=Please%20go%20through%20https%3A%2F%2Fwiki.mozilla.org%2FBlocklisting%2FDLL%20before%20filing%20a%20new%20bug. a bug] if it does not exist.
# Answer all the questions in [https://msmania.github.io/assets/mozilla/third-party-modules/questionnaire.txt 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
 
=== Syntax ===
 
  Variable += [
  ...
      # One-liner comment including a bug number
      EntryType(Name, Version, Flags),
  ...
  ]
 
=== Parameters ===
 
{| class="wikitable"
|-
! Parameter !! Value
|-
| Variable ||
'''ALL_PROCESSES''' | '''BROWSER_PROCESS''' | '''CHILD_PROCESSES'''
|-
| EntryType ||
'''DllBlocklistEntry''' | '''A11yBlocklistEntry''' | '''RedirectToNoOpEntryPoint'''
|-
| Name || A string representing a DLL's filename to block
|-
| Version || One of the following formats:
* '''ALL_VERSIONS''' | '''UNVERSIONED'''
* A tuple consisting of four digits
* A 32-bit integer representing a Unix timestamp with '''PETimeStamp'''
|-
| Flags ||
'''BLOCK_WIN8_AND_OLDER''' | '''BLOCK_WIN7_AND_OLDER'''
|}
 
==== Variable ====
 
Choose one of the following predefined variables.
 
* '''ALL_PROCESSES''':<br />DLLs defined here are blocked in '''BROWSER_PROCESS''' + '''CHILD_PROCESSES'''
* '''BROWSER_PROCESS'''<br />DLLs defined here are blocked in the browser process
* '''CHILD_PROCESSES'''<br />DLLs defined here are blocked in non-browser processes
 
==== EntryType ====
 
Choose one of the following predefined EntryTypes.
 
* '''DllBlocklistEntry'''<br />Use this EntryType unless your case matches the other EntryTypes.
* '''A11yBlocklistEntry'''<br />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.
* '''RedirectToNoOpEntryPoint'''<br />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.
 
==== Name ====
 
A string representing a DLL's filename to block.  Don’t include a directory name.
 
==== Version ====
 
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'''.<br />
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.<br />Example: (1, 2, 3, 4)
* A 32-bit integer representing a Unix timestamp with '''PETimeStamp'''.  This is compared to an integer of [https://docs.microsoft.com/en-us/windows/win32/api/winnt/ns-winnt-image_file_header IMAGE_FILE_HEADER]::TimeDateStamp.<br />Example: PETimeStamp(0x12345678)
 
==== Flags ====
 
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.
 
* '''BLOCK_WIN8_AND_OLDER'''
* '''BLOCK_WIN7_AND_OLDER'''
 
== 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 [https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-directory-table Import Directory Table]<br />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 [https://docs.microsoft.com/en-us/windows/win32/winsock/categorizing-layered-service-providers-and-applications Layered Service Provider]<br />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 [https://docs.microsoft.com/en-us/windows/win32/winmsg/hooks Window hook]<br />Blocking this type of module causes repetitive attempts to load a module, resulting in slow performance like [https://bugzilla.mozilla.org/show_bug.cgi?id=1633718 Bug 1633718].
 
== Third-party-module ping ==
 
We’re collecting [https://firefox-source-docs.mozilla.org/toolkit/components/telemetry/data/third-party-modules-ping.html 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.<br />(BigQuery console is much faster and can handle larger data.)<br />- BigQuery console (visit [https://docs.telemetry.mozilla.org/cookbooks/bigquery.html#gcp-bigquery-console here] to request access): https://console.cloud.google.com/bigquery<br />- STMO: https://sql.telemetry.mozilla.org/
# Make your own query based on [https://msmania.github.io/assets/mozilla/third-party-modules/query-template.txt this template].
# Run the query.
# Save the result as a JSON file.<br />- In BigQuery console, click [SAVE RESULTS] and choose [JSON (local file)].<br />- 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/<br />(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 [https://msmania.github.io/assets/mozilla/third-party-modules/query-groupby-template.txt 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.
 
== Contact ==
 
Any question or feedback is welcome.
 
'''Matrix/Riot.IM''': [https://mozilla.riot.im/#/room/#hardening:mozilla.org #hardening]

Latest revision as of 16:39, 3 January 2023