1 // Copyright 2011 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/native_library.h"
9 #include "base/files/file_util.h"
10 #include "base/metrics/histogram_macros.h"
11 #include "base/path_service.h"
12 #include "base/scoped_native_library.h"
13 #include "base/strings/strcat.h"
14 #include "base/strings/string_piece.h"
15 #include "base/strings/string_util.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/threading/scoped_blocking_call.h"
19 #include "base/threading/scoped_thread_priority.h"
25 // This enum is used to back an UMA histogram, and should therefore be treated
27 enum LoadLibraryResult {
28 // LoadLibraryExW API/flags are available and the call succeeds.
30 // LoadLibraryExW API/flags are availabe to use but the call fails, then
31 // LoadLibraryW is used and succeeds.
33 // LoadLibraryExW API/flags are availabe to use but the call fails, then
34 // LoadLibraryW is used but fails as well.
36 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
38 UNAVAILABLE_AND_SUCCEED,
39 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
42 // Add new items before this one, always keep this one at the end.
46 // A helper method to log library loading result to UMA.
47 void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
48 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
49 LoadLibraryResult::END);
52 // A helper method to check if AddDllDirectory method is available, thus
53 // LOAD_LIBRARY_SEARCH_* flags are available on systems.
54 bool AreSearchFlagsAvailable() {
55 // The LOAD_LIBRARY_SEARCH_* flags are available on systems that have
56 // KB2533623 installed. To determine whether the flags are available, use
57 // GetProcAddress to get the address of the AddDllDirectory,
58 // RemoveDllDirectory, or SetDefaultDllDirectories function. If GetProcAddress
59 // succeeds, the LOAD_LIBRARY_SEARCH_* flags can be used with LoadLibraryEx.
60 // https://msdn.microsoft.com/en-us/library/windows/desktop/ms684179(v=vs.85).aspx
61 // The LOAD_LIBRARY_SEARCH_* flags are used in the LoadNativeLibraryHelper
63 static const auto add_dll_dir_func =
64 reinterpret_cast<decltype(::AddDllDirectory)*>(
65 GetProcAddress(GetModuleHandle(L"kernel32.dll"), "AddDllDirectory"));
66 return !!add_dll_dir_func;
69 // A helper method to encode the library loading result to enum
71 LoadLibraryResult GetLoadLibraryResult(bool are_search_flags_available,
72 bool has_load_library_succeeded) {
73 LoadLibraryResult result;
74 if (are_search_flags_available) {
75 if (has_load_library_succeeded)
76 result = LoadLibraryResult::FAIL_AND_SUCCEED;
78 result = LoadLibraryResult::FAIL_AND_FAIL;
79 } else if (has_load_library_succeeded) {
80 result = LoadLibraryResult::UNAVAILABLE_AND_SUCCEED;
82 result = LoadLibraryResult::UNAVAILABLE_AND_FAIL;
87 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
88 NativeLibraryLoadError* error) {
89 // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
90 // must not be called from DllMain.
91 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
93 // Mitigate the issues caused by loading DLLs on a background thread
94 // (see http://crbug/973868 for context). This temporarily boosts this
95 // thread's priority so that it doesn't get starved by higher priority threads
96 // while it holds the LoaderLock.
97 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
99 HMODULE module = nullptr;
101 // This variable records the library loading result.
102 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
104 bool are_search_flags_available = AreSearchFlagsAvailable();
105 if (are_search_flags_available) {
106 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
107 // directory as the library may have dependencies on DLLs in this
109 module = ::LoadLibraryExW(
110 library_path.value().c_str(), nullptr,
111 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
112 // If LoadLibraryExW succeeds, log this metric and return.
114 LogLibrarayLoadResultToUMA(load_library_result);
117 // GetLastError() needs to be called immediately after
118 // LoadLibraryExW call.
120 error->code = ::GetLastError();
123 // If LoadLibraryExW API/flags are unavailable or API call fails, try
124 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
126 // Switch the current directory to the library directory as the library
127 // may have dependencies on DLLs in this directory.
128 bool restore_directory = false;
129 FilePath current_directory;
130 if (GetCurrentDirectory(¤t_directory)) {
131 FilePath plugin_path = library_path.DirName();
132 if (!plugin_path.empty()) {
133 SetCurrentDirectory(plugin_path);
134 restore_directory = true;
137 module = ::LoadLibraryW(library_path.value().c_str());
139 // GetLastError() needs to be called immediately after LoadLibraryW call.
140 if (!module && error)
141 error->code = ::GetLastError();
143 if (restore_directory)
144 SetCurrentDirectory(current_directory);
146 // Get the library loading result and log it to UMA.
147 LogLibrarayLoadResultToUMA(
148 GetLoadLibraryResult(are_search_flags_available, !!module));
153 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
154 NativeLibraryLoadError* error) {
155 // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
156 // hence must not be called from Dllmain.
157 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
158 NativeLibrary module;
160 ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
162 bool are_search_flags_available = AreSearchFlagsAvailable();
163 // Prefer LOAD_LIBRARY_SEARCH_SYSTEM32 to avoid DLL preloading attacks.
164 DWORD flags = are_search_flags_available ? LOAD_LIBRARY_SEARCH_SYSTEM32
165 : LOAD_WITH_ALTERED_SEARCH_PATH;
166 module = ::LoadLibraryExW(library_path.value().c_str(), nullptr, flags);
168 if (!module && error)
169 error->code = ::GetLastError();
171 LogLibrarayLoadResultToUMA(
172 GetLoadLibraryResult(are_search_flags_available, !!module));
178 FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
179 FilePath library_path;
180 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
181 if (PathService::Get(DIR_SYSTEM, &library_path))
182 library_path = library_path.Append(name);
188 std::string NativeLibraryLoadError::ToString() const {
189 return StringPrintf("%lu", code);
192 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
193 const NativeLibraryOptions& options,
194 NativeLibraryLoadError* error) {
195 return LoadNativeLibraryHelper(library_path, error);
198 void UnloadNativeLibrary(NativeLibrary library) {
199 FreeLibrary(library);
202 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
204 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
207 std::string GetNativeLibraryName(StringPiece name) {
208 DCHECK(IsStringASCII(name));
209 return StrCat({name, ".dll"});
212 std::string GetLoadableModuleName(StringPiece name) {
213 return GetNativeLibraryName(name);
216 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
217 NativeLibraryLoadError* error) {
218 FilePath library_path = GetSystemLibraryName(name);
219 if (library_path.empty()) {
221 error->code = ERROR_NOT_FOUND;
224 return LoadSystemLibraryHelper(library_path, error);
227 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
228 NativeLibraryLoadError* error) {
229 FilePath library_path = GetSystemLibraryName(name);
230 if (library_path.empty()) {
232 error->code = ERROR_NOT_FOUND;
236 // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
238 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
239 ScopedNativeLibrary module;
240 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
241 library_path.value().c_str(),
242 ScopedNativeLibrary::Receiver(module).get())) {
243 return module.release();
246 // Load and pin the library since it wasn't already loaded.
247 module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
248 if (!module.is_valid())
251 ScopedNativeLibrary temp;
252 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
253 library_path.value().c_str(),
254 ScopedNativeLibrary::Receiver(temp).get())) {
255 return module.release();
259 error->code = ::GetLastError();
260 // Return nullptr since we failed to pin the module.