123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264 |
- // Copyright (c) 2011 The Chromium Authors. All rights reserved.
- // Use of this source code is governed by a BSD-style license that can be
- // found in the LICENSE file.
- #include "base/native_library.h"
- #include <windows.h>
- #include "base/files/file_util.h"
- #include "base/metrics/histogram_macros.h"
- #include "base/path_service.h"
- #include "base/scoped_native_library.h"
- #include "base/strings/strcat.h"
- #include "base/strings/string_piece.h"
- #include "base/strings/string_util.h"
- #include "base/strings/stringprintf.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/threading/scoped_blocking_call.h"
- #include "base/threading/scoped_thread_priority.h"
- namespace base {
- namespace {
- // This enum is used to back an UMA histogram, and should therefore be treated
- // as append-only.
- enum LoadLibraryResult {
- // LoadLibraryExW API/flags are available and the call succeeds.
- SUCCEED = 0,
- // LoadLibraryExW API/flags are availabe to use but the call fails, then
- // LoadLibraryW is used and succeeds.
- FAIL_AND_SUCCEED,
- // LoadLibraryExW API/flags are availabe to use but the call fails, then
- // LoadLibraryW is used but fails as well.
- FAIL_AND_FAIL,
- // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
- // and succeeds.
- UNAVAILABLE_AND_SUCCEED,
- // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
- // but fails.
- UNAVAILABLE_AND_FAIL,
- // Add new items before this one, always keep this one at the end.
- END
- };
- // A helper method to log library loading result to UMA.
- void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
- UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
- LoadLibraryResult::END);
- }
- // A helper method to check if AddDllDirectory method is available, thus
- // LOAD_LIBRARY_SEARCH_* flags are available on systems.
- bool AreSearchFlagsAvailable() {
- // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have
- // KB2533623 installed. To determine whether the flags are available, use
- // GetProcAddress to get the address of the AddDllDirectory,
- // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress
- // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx.
- // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
- // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper
- // method.
- static const auto add_dll_dir_func =
- reinterpret_cast<decltype(::AddDllDirectory)*>(
- GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory"));
- return !!add_dll_dir_func;
- }
- // A helper method to encode the library loading result to enum
- // LoadLibraryResult.
- LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available,
- bool has_load_library_succeeded) {
- LoadLibraryResult result;
- if (are_search_flags_available) {
- if (has_load_library_succeeded)
- result = LoadLibraryResult::FAIL_AND_SUCCEED;
- else
- result = LoadLibraryResult::FAIL_AND_FAIL;
- } else if (has_load_library_succeeded) {
- result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED;
- } else {
- result = LoadLibraryResult::UNAVAILABLE_AND_FAIL;
- }
- return result;
- }
- NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
- NativeLibraryLoadError* error) {
- // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
- // must not be called from DllMain.
- ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
- // Mitigate the issues caused by loading DLLs on a background thread
- // (see http://crbug/973868 for context). This temporarily boosts this
- // thread's priority so that it doesn't get starved by higher priority threads
- // while it holds the LoaderLock.
- SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
- HMODULE module = nullptr;
- // This variable records the library loading result.
- LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
- bool are_search_flags_available = AreSearchFlagsAvailable();
- if (are_search_flags_available) {
- // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
- // directory as the library may have dependencies on DLLs in this
- // directory.
- module = ::LoadLibraryExW(
- library_path.value().c_str(), nullptr,
- LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
- // If LoadLibraryExW succeeds, log this metric and return.
- if (module) {
- LogLibrarayLoadResultToUMA(load_library_result);
- return module;
- }
- // GetLastError() needs to be called immediately after
- // LoadLibraryExW call.
- if (error)
- error->code = ::GetLastError();
- }
- // If LoadLibraryExW API/flags are unavailable or API call fails, try
- // LoadLibraryW API. From UMA, this fallback is necessary for many users.
- // Switch the current directory to the library directory as the library
- // may have dependencies on DLLs in this directory.
- bool restore_directory = false;
- FilePath current_directory;
- if (GetCurrentDirectory(¤t_directory)) {
- FilePath plugin_path = library_path.DirName();
- if (!plugin_path.empty()) {
- SetCurrentDirectory(plugin_path);
- restore_directory = true;
- }
- }
- module = ::LoadLibraryW(library_path.value().c_str());
- // GetLastError() needs to be called immediately after LoadLibraryW call.
- if (!module && error)
- error->code = ::GetLastError();
- if (restore_directory)
- SetCurrentDirectory(current_directory);
- // Get the library loading result and log it to UMA.
- LogLibrarayLoadResultToUMA(
- GetLoadLibraryResult(are_search_flags_available, !!module));
- return module;
- }
- NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
- NativeLibraryLoadError* error) {
- // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
- // hence must not be called from Dllmain.
- ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
- NativeLibrary module;
- BOOL module_found =
- ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
- if (!module_found) {
- bool are_search_flags_available = AreSearchFlagsAvailable();
- // Prefer LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL preloading attacks.
- DWORD flags = are_search_flags_available ? LOAD_LIBRARY_SEARCH_SYSTEM32
- : LOAD_WITH_ALTERED_SEARCH_PATH;
- module = ::LoadLibraryExW(library_path.value().c_str(), nullptr, flags);
- if (!module && error)
- error->code = ::GetLastError();
- LogLibrarayLoadResultToUMA(
- GetLoadLibraryResult(are_search_flags_available, !!module));
- }
- return module;
- }
- FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
- FilePath library_path;
- // Use an absolute path to load the DLL to avoid DLL preloading attacks.
- if (PathService::Get(DIR_SYSTEM, &library_path))
- library_path = library_path.Append(name);
- return library_path;
- }
- } // namespace
- std::string NativeLibraryLoadError::ToString() const {
- return StringPrintf("%lu", code);
- }
- NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
- const NativeLibraryOptions& options,
- NativeLibraryLoadError* error) {
- return LoadNativeLibraryHelper(library_path, error);
- }
- void UnloadNativeLibrary(NativeLibrary library) {
- FreeLibrary(library);
- }
- void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
- StringPiece name) {
- return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
- }
- std::string GetNativeLibraryName(StringPiece name) {
- DCHECK(IsStringASCII(name));
- return StrCat({name, ".dll"});
- }
- std::string GetLoadableModuleName(StringPiece name) {
- return GetNativeLibraryName(name);
- }
- NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
- NativeLibraryLoadError* error) {
- FilePath library_path = GetSystemLibraryName(name);
- if (library_path.empty()) {
- if (error)
- error->code = ERROR_NOT_FOUND;
- return nullptr;
- }
- return LoadSystemLibraryHelper(library_path, error);
- }
- NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
- NativeLibraryLoadError* error) {
- FilePath library_path = GetSystemLibraryName(name);
- if (library_path.empty()) {
- if (error)
- error->code = ERROR_NOT_FOUND;
- return nullptr;
- }
- // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
- // Dllmain.
- ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
- ScopedNativeLibrary module;
- if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
- library_path.value().c_str(),
- ScopedNativeLibrary::Receiver(module).get())) {
- return module.release();
- }
- // Load and pin the library since it wasn't already loaded.
- module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
- if (!module.is_valid())
- return nullptr;
- ScopedNativeLibrary temp;
- if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
- library_path.value().c_str(),
- ScopedNativeLibrary::Receiver(temp).get())) {
- return module.release();
- }
- if (error)
- error->code = ::GetLastError();
- // Return nullptr since we failed to pin the module.
- return nullptr;
- }
- } // namespace base
|