• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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(&current_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