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.
4
5 #include "base/native_library.h"
6
7 #include <windows.h>
8
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"
20
21 namespace base {
22
23 namespace {
24
25 // This enum is used to back an UMA histogram, and should therefore be treated
26 // as append-only.
27 enum LoadLibraryResult {
28 // LoadLibraryExW API/flags are available and the call succeeds.
29 SUCCEED = 0,
30 // LoadLibraryExW API/flags are availabe to use but the call fails, then
31 // LoadLibraryW is used and succeeds.
32 FAIL_AND_SUCCEED,
33 // LoadLibraryExW API/flags are availabe to use but the call fails, then
34 // LoadLibraryW is used but fails as well.
35 FAIL_AND_FAIL,
36 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
37 // and succeeds. Pre-Win10-only.
38 UNAVAILABLE_AND_SUCCEED_OBSOLETE,
39 // LoadLibraryExW API/flags are unavailabe to use, then LoadLibraryW is used
40 // but fails. Pre-Win10-only.
41 UNAVAILABLE_AND_FAIL_OBSOLETE,
42 // Add new items before this one, always keep this one at the end.
43 END
44 };
45
46 // A helper method to log library loading result to UMA.
LogLibrarayLoadResultToUMA(LoadLibraryResult result)47 void LogLibrarayLoadResultToUMA(LoadLibraryResult result) {
48 UMA_HISTOGRAM_ENUMERATION("LibraryLoader.LoadNativeLibraryWindows", result,
49 LoadLibraryResult::END);
50 }
51
52 // A helper method to encode the library loading result to enum
53 // LoadLibraryResult.
GetLoadLibraryResult(bool has_load_library_succeeded)54 LoadLibraryResult GetLoadLibraryResult(bool has_load_library_succeeded) {
55 return has_load_library_succeeded ? LoadLibraryResult::FAIL_AND_SUCCEED
56 : LoadLibraryResult::FAIL_AND_FAIL;
57 }
58
LoadNativeLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)59 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
60 NativeLibraryLoadError* error) {
61 // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
62 // must not be called from DllMain.
63 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
64
65 // Mitigate the issues caused by loading DLLs on a background thread
66 // (see http://crbug/973868 for context). This temporarily boosts this
67 // thread's priority so that it doesn't get starved by higher priority threads
68 // while it holds the LoaderLock.
69 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
70
71 HMODULE module_handle = nullptr;
72
73 // This variable records the library loading result.
74 LoadLibraryResult load_library_result = LoadLibraryResult::SUCCEED;
75
76 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
77 // directory as the library may have dependencies on DLLs in this
78 // directory.
79 module_handle = ::LoadLibraryExW(
80 library_path.value().c_str(), nullptr,
81 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
82 // If LoadLibraryExW succeeds, log this metric and return.
83 if (module_handle) {
84 LogLibrarayLoadResultToUMA(load_library_result);
85 return module_handle;
86 }
87 // GetLastError() needs to be called immediately after
88 // LoadLibraryExW call.
89 if (error) {
90 error->code = ::GetLastError();
91 }
92
93 // If LoadLibraryExW API/flags are unavailable or API call fails, try
94 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
95
96 // Switch the current directory to the library directory as the library
97 // may have dependencies on DLLs in this directory.
98 bool restore_directory = false;
99 FilePath current_directory;
100 if (GetCurrentDirectory(¤t_directory)) {
101 FilePath plugin_path = library_path.DirName();
102 if (!plugin_path.empty()) {
103 SetCurrentDirectory(plugin_path);
104 restore_directory = true;
105 }
106 }
107 module_handle = ::LoadLibraryW(library_path.value().c_str());
108
109 // GetLastError() needs to be called immediately after LoadLibraryW call.
110 if (!module_handle && error) {
111 error->code = ::GetLastError();
112 }
113
114 if (restore_directory)
115 SetCurrentDirectory(current_directory);
116
117 // Get the library loading result and log it to UMA.
118 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module_handle));
119
120 return module_handle;
121 }
122
LoadSystemLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)123 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
124 NativeLibraryLoadError* error) {
125 // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
126 // hence must not be called from Dllmain.
127 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
128 NativeLibrary module;
129 BOOL module_found =
130 ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
131 if (!module_found) {
132 module = ::LoadLibraryExW(library_path.value().c_str(), nullptr,
133 LOAD_LIBRARY_SEARCH_SYSTEM32);
134
135 if (!module && error)
136 error->code = ::GetLastError();
137
138 LogLibrarayLoadResultToUMA(GetLoadLibraryResult(!!module));
139 }
140
141 return module;
142 }
143
GetSystemLibraryName(FilePath::StringPieceType name)144 FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
145 FilePath library_path;
146 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
147 if (PathService::Get(DIR_SYSTEM, &library_path))
148 library_path = library_path.Append(name);
149 return library_path;
150 }
151
152 } // namespace
153
ToString() const154 std::string NativeLibraryLoadError::ToString() const {
155 return StringPrintf("%lu", code);
156 }
157
LoadNativeLibraryWithOptions(const FilePath & library_path,const NativeLibraryOptions & options,NativeLibraryLoadError * error)158 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
159 const NativeLibraryOptions& options,
160 NativeLibraryLoadError* error) {
161 return LoadNativeLibraryHelper(library_path, error);
162 }
163
UnloadNativeLibrary(NativeLibrary library)164 void UnloadNativeLibrary(NativeLibrary library) {
165 FreeLibrary(library);
166 }
167
GetFunctionPointerFromNativeLibrary(NativeLibrary library,StringPiece name)168 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
169 StringPiece name) {
170 return reinterpret_cast<void*>(GetProcAddress(library, name.data()));
171 }
172
GetNativeLibraryName(StringPiece name)173 std::string GetNativeLibraryName(StringPiece name) {
174 DCHECK(IsStringASCII(name));
175 return StrCat({name, ".dll"});
176 }
177
GetLoadableModuleName(StringPiece name)178 std::string GetLoadableModuleName(StringPiece name) {
179 return GetNativeLibraryName(name);
180 }
181
LoadSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)182 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
183 NativeLibraryLoadError* error) {
184 FilePath library_path = GetSystemLibraryName(name);
185 if (library_path.empty()) {
186 if (error)
187 error->code = ERROR_NOT_FOUND;
188 return nullptr;
189 }
190 return LoadSystemLibraryHelper(library_path, error);
191 }
192
PinSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)193 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
194 NativeLibraryLoadError* error) {
195 FilePath library_path = GetSystemLibraryName(name);
196 if (library_path.empty()) {
197 if (error)
198 error->code = ERROR_NOT_FOUND;
199 return nullptr;
200 }
201
202 // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
203 // Dllmain.
204 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
205 ScopedNativeLibrary module;
206 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
207 library_path.value().c_str(),
208 ScopedNativeLibrary::Receiver(module).get())) {
209 return module.release();
210 }
211
212 // Load and pin the library since it wasn't already loaded.
213 module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
214 if (!module.is_valid())
215 return nullptr;
216
217 ScopedNativeLibrary temp;
218 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
219 library_path.value().c_str(),
220 ScopedNativeLibrary::Receiver(temp).get())) {
221 return module.release();
222 }
223
224 if (error)
225 error->code = ::GetLastError();
226 // Return nullptr since we failed to pin the module.
227 return nullptr;
228 }
229
230 } // namespace base
231