[go: nahoru, domu]

Ensure user32 and gdi32 do not load in the renderer process

The sandboxed renderer process is explicitly blocked from calling into
user32 and gdi32 as a result of the Win32k lockdown. As such, there's no
reason to even load those dlls in the renderer process. By delayloading
all of the dlls necessary to prevent user32 and gdi32 from ever loading
into the renderer process we can achieve significant performance
improvements by reducing memory usage in every process.

This change adds many delayloads to chrome.exe, chrome.dll, and
chrome_child.dll. The most important ones are user32, gdi32, shell32,
ole32, and shwlapi, but there's additional ones included because they
also reference one of the above so the entire chain needs to be done.

There's also delayloads added to a few different component dlls,
however this change unfortunately does not completely fix the delayloads
in a component build because the the base_win_linker_flags cannot be
overridden and the sbox_integration_tests and sbox_validation_tests both
require the dlls to be statically linked. The reason they must be
statically linked is because of an OS bug related to the delayload
handler, as described below. It is prohibitively complicated to add the
delayloads to every single dll that depends on base, which is why it
hasn't been done.

One of the biggest considerations for this change is that there is an OS
bug in all versions of Windows which causes LoadLibrary to fail and the
delayload handler to crash with error code c06d007e
(ERROR_MOD_NOT_FOUND) when they run inside the sandboxed
process. The issue here is that there is an access check that fails due
to the ACL restrictions of the sandboxed process. While this OS bug is
unfortunate, it doesn't prevent this change from occurring; rather it
just requires that this change ensures no code executes which results
in the delayload handler from running. As such, this change adds calls
to IsUser32AndGdi32Available and updates all the calls that use
GetProcAddress into user32.dll to do proper LoadLibrary calls first.
To help make this cleaner, easier, and more consistent, two new methods
were created. First, PinUser32() will load user32.dll if not loaded and
ensure it is pinned (using a new PinSystemLibrary method) and cannot be
unloaded (as there's no good reason to let it unload). It also caches
the module handle to avoid subsequent calls to either GetModuleHandleEx
or LoadLibraryEx as those can be potentially blocking and are definitely
unnecessary once the module is pinned. The second method is
GetUser32FunctionPointer which simplifies the process of callers
retrieving function pointers by wrapping and hiding the LoadLibrary
requirements.

The OS bug which causes a crash actually helped with validation, as this
allowed for longhaul testing on private builds looking specifically for
crashes. This resulted in updates to this change as well as
separate changes that have been made to blink, pdfium, and v8 to remove
invalid method calls and dll references.

A critical part of this change is the update to
CommandLine::ParseFromString(). In this change the command line handler
explicitly loads api-ms-win-downlevel-shell32-l1-1-0.dll and finds
CommandLineToArgvW via the apiset if possible. This is necessary because
the standard delayload handler would normally find this method in
shell32, however loading shell32 brings in user32 and this prevents the
entire change from working. However, the actual implementation is in
shcore.dll, not shell32.dll, and the apiset is able to do this
forwarding and avoid shell32.dll entirely. This only works on Win10,
unfortunately, which is one reason why the performance benefits of this
change can only be accomplished on Win10, not Win7. On Win7, the
organization and linking of the system dlls will always bring in
user32.dll.

Finally, this change also adds a new test library, delayloads_unittests,
which is modeled after the existing chrome_elf_import_unittests, which
ensures the lists of statically linked dlls for the three main binaries
cannot be accidentally updated to break this change and it ensures the
dlls can be properly loaded without bringing in user32.

We've run some perf comparisons on this change and our findings show a
reduction in private COW pages (actual, committed private memory) of
~500K per renderer process!

Bug: 948829
Change-Id: I9eab10508866bc09e2f78abbe42e51d67f417186
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1551709
Reviewed-by: Greg Thompson <grt@chromium.org>
Reviewed-by: Scott Violet <sky@chromium.org>
Reviewed-by: Bruce Dawson <brucedawson@chromium.org>
Reviewed-by: François Doray <fdoray@chromium.org>
Commit-Queue: Cliff Smolinsky <cliffsmo@microsoft.com>
Cr-Commit-Position: refs/heads/master@{#656516}
24 files changed