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 <string_view>
10
11 #include "base/files/file_util.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/path_service.h"
14 #include "base/scoped_native_library.h"
15 #include "base/strings/strcat.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/threading/scoped_blocking_call.h"
20 #include "base/threading/scoped_thread_priority.h"
21
22 namespace base {
23
24 namespace {
25
LoadNativeLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)26 NativeLibrary LoadNativeLibraryHelper(const FilePath& library_path,
27 NativeLibraryLoadError* error) {
28 // LoadLibrary() opens the file off disk and acquires the LoaderLock, hence
29 // must not be called from DllMain.
30 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
31
32 // Mitigate the issues caused by loading DLLs on a background thread
33 // (see http://crbug/973868 for context). This temporarily boosts this
34 // thread's priority so that it doesn't get starved by higher priority threads
35 // while it holds the LoaderLock.
36 SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY_REPEATEDLY();
37
38 HMODULE module_handle = nullptr;
39
40 // LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR flag is needed to search the library
41 // directory as the library may have dependencies on DLLs in this
42 // directory.
43 module_handle = ::LoadLibraryExW(
44 library_path.value().c_str(), nullptr,
45 LOAD_LIBRARY_SEARCH_DLL_LOAD_DIR | LOAD_LIBRARY_SEARCH_DEFAULT_DIRS);
46 // If LoadLibraryExW succeeds, log this metric and return.
47 if (module_handle) {
48 return module_handle;
49 }
50 // GetLastError() needs to be called immediately after
51 // LoadLibraryExW call.
52 if (error) {
53 error->code = ::GetLastError();
54 }
55
56 // If LoadLibraryExW API/flags are unavailable or API call fails, try
57 // LoadLibraryW API. From UMA, this fallback is necessary for many users.
58
59 // Switch the current directory to the library directory as the library
60 // may have dependencies on DLLs in this directory.
61 bool restore_directory = false;
62 FilePath current_directory;
63 if (GetCurrentDirectory(¤t_directory)) {
64 FilePath plugin_path = library_path.DirName();
65 if (!plugin_path.empty()) {
66 SetCurrentDirectory(plugin_path);
67 restore_directory = true;
68 }
69 }
70 module_handle = ::LoadLibraryW(library_path.value().c_str());
71
72 // GetLastError() needs to be called immediately after LoadLibraryW call.
73 if (!module_handle && error) {
74 error->code = ::GetLastError();
75 }
76
77 if (restore_directory)
78 SetCurrentDirectory(current_directory);
79
80 return module_handle;
81 }
82
LoadSystemLibraryHelper(const FilePath & library_path,NativeLibraryLoadError * error)83 NativeLibrary LoadSystemLibraryHelper(const FilePath& library_path,
84 NativeLibraryLoadError* error) {
85 // GetModuleHandleEx and subsequently LoadLibraryEx acquire the LoaderLock,
86 // hence must not be called from Dllmain.
87 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
88 NativeLibrary module;
89 BOOL module_found =
90 ::GetModuleHandleExW(0, library_path.value().c_str(), &module);
91 if (!module_found) {
92 module = ::LoadLibraryExW(library_path.value().c_str(), nullptr,
93 LOAD_LIBRARY_SEARCH_SYSTEM32);
94
95 if (!module && error)
96 error->code = ::GetLastError();
97 }
98
99 return module;
100 }
101
GetSystemLibraryName(FilePath::StringPieceType name)102 FilePath GetSystemLibraryName(FilePath::StringPieceType name) {
103 FilePath library_path;
104 // Use an absolute path to load the DLL to avoid DLL preloading attacks.
105 if (PathService::Get(DIR_SYSTEM, &library_path))
106 library_path = library_path.Append(name);
107 return library_path;
108 }
109
110 } // namespace
111
ToString() const112 std::string NativeLibraryLoadError::ToString() const {
113 return StringPrintf("%lu", code);
114 }
115
LoadNativeLibraryWithOptions(const FilePath & library_path,const NativeLibraryOptions & options,NativeLibraryLoadError * error)116 NativeLibrary LoadNativeLibraryWithOptions(const FilePath& library_path,
117 const NativeLibraryOptions& options,
118 NativeLibraryLoadError* error) {
119 return LoadNativeLibraryHelper(library_path, error);
120 }
121
UnloadNativeLibrary(NativeLibrary library)122 void UnloadNativeLibrary(NativeLibrary library) {
123 FreeLibrary(library);
124 }
125
GetFunctionPointerFromNativeLibrary(NativeLibrary library,const char * name)126 void* GetFunctionPointerFromNativeLibrary(NativeLibrary library,
127 const char* name) {
128 return reinterpret_cast<void*>(GetProcAddress(library, name));
129 }
130
GetNativeLibraryName(std::string_view name)131 std::string GetNativeLibraryName(std::string_view name) {
132 DCHECK(IsStringASCII(name));
133 return StrCat({name, ".dll"});
134 }
135
GetLoadableModuleName(std::string_view name)136 std::string GetLoadableModuleName(std::string_view name) {
137 return GetNativeLibraryName(name);
138 }
139
LoadSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)140 NativeLibrary LoadSystemLibrary(FilePath::StringPieceType name,
141 NativeLibraryLoadError* error) {
142 FilePath library_path = GetSystemLibraryName(name);
143 if (library_path.empty()) {
144 if (error)
145 error->code = ERROR_NOT_FOUND;
146 return nullptr;
147 }
148 return LoadSystemLibraryHelper(library_path, error);
149 }
150
PinSystemLibrary(FilePath::StringPieceType name,NativeLibraryLoadError * error)151 NativeLibrary PinSystemLibrary(FilePath::StringPieceType name,
152 NativeLibraryLoadError* error) {
153 FilePath library_path = GetSystemLibraryName(name);
154 if (library_path.empty()) {
155 if (error)
156 error->code = ERROR_NOT_FOUND;
157 return nullptr;
158 }
159
160 // GetModuleHandleEx acquires the LoaderLock, hence must not be called from
161 // Dllmain.
162 ScopedBlockingCall scoped_blocking_call(FROM_HERE, BlockingType::MAY_BLOCK);
163 ScopedNativeLibrary module;
164 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
165 library_path.value().c_str(),
166 ScopedNativeLibrary::Receiver(module).get())) {
167 return module.release();
168 }
169
170 // Load and pin the library since it wasn't already loaded.
171 module = ScopedNativeLibrary(LoadSystemLibraryHelper(library_path, error));
172 if (!module.is_valid())
173 return nullptr;
174
175 ScopedNativeLibrary temp;
176 if (::GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_PIN,
177 library_path.value().c_str(),
178 ScopedNativeLibrary::Receiver(temp).get())) {
179 return module.release();
180 }
181
182 if (error)
183 error->code = ::GetLastError();
184 // Return nullptr since we failed to pin the module.
185 return nullptr;
186 }
187
188 } // namespace base
189