native_library_win.cc 9.5 KB

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