gdi_debug_util_win.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. // Copyright 2014 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/debug/gdi_debug_util_win.h"
  5. #include <algorithm>
  6. #include <cmath>
  7. #include <TlHelp32.h>
  8. #include <psapi.h>
  9. #include <stddef.h>
  10. #include <windows.h>
  11. #include <winternl.h>
  12. #include "base/debug/alias.h"
  13. #include "base/logging.h"
  14. #include "base/process/process.h"
  15. #include "base/win/scoped_handle.h"
  16. #include "base/win/win_util.h"
  17. #include "base/win/windows_version.h"
  18. #include "third_party/abseil-cpp/absl/types/optional.h"
  19. namespace {
  20. // A partial PEB up until GdiSharedHandleTable.
  21. // Derived from the ntdll symbols (ntdll!_PEB).
  22. template <typename PointerType>
  23. struct PartialWinPeb {
  24. unsigned char InheritedAddressSpace;
  25. unsigned char ReadImageFileExecOptions;
  26. unsigned char BeingDebugged;
  27. unsigned char ImageUsesLargePages : 1;
  28. unsigned char IsProtectedProcess : 1;
  29. unsigned char IsLegacyProcess : 1;
  30. unsigned char IsImageDynamicallyRelocated : 1;
  31. unsigned char SkipPatchingUser32Forwarders : 1;
  32. unsigned char IsAppContainer : 1;
  33. unsigned char IsProtectedProcessLight : 1;
  34. unsigned char IsLongPathAwareProcess : 1;
  35. PointerType Mutant;
  36. PointerType ImageBaseAddress;
  37. PointerType Ldr;
  38. PointerType ProcessParamters;
  39. PointerType SubSystemData;
  40. PointerType ProcessHeap;
  41. PointerType FastPebLock;
  42. PointerType AtlThunkSListPtr;
  43. PointerType IFEOKey;
  44. uint32_t ProcessInJob : 1;
  45. uint32_t ProcessInitializing : 1;
  46. uint32_t ProcessUsingVEH : 1;
  47. uint32_t ProcessUsingVCH : 1;
  48. uint32_t ProcessUsingFTH : 1;
  49. uint32_t ProcessPreviouslyThrottled : 1;
  50. uint32_t ProcessCurrentlyThrottled : 1;
  51. uint32_t ProcessImagesHotPatched : 1;
  52. PointerType KernelCallbackTable;
  53. uint32_t SystemReserved;
  54. uint32_t AtlThunkSListPtr32;
  55. PointerType ApiSetMap;
  56. uint32_t TlsExpansionCounter;
  57. PointerType TlsBitmap;
  58. uint32_t TlsBitmapBits[2];
  59. PointerType ReadOnlySharedMemoryBase;
  60. PointerType HotpatchInformation;
  61. PointerType ReadOnlyStaticServerData;
  62. PointerType AnsiCodePageData;
  63. PointerType OemCodePageData;
  64. PointerType UnicodeCaseTableData;
  65. uint32_t NumberOfProcessors;
  66. uint32_t NtGlobalFlag;
  67. uint64_t CriticalSectionTimeout;
  68. PointerType HeapSegmentReserve;
  69. PointerType HeapSegmentCommit;
  70. PointerType HeapDeCommitTotalFreeThreshold;
  71. PointerType HeapDeCommitFreeBlockThreshold;
  72. uint32_t NumberOfHeaps;
  73. uint32_t MaximumNumberOfHeaps;
  74. PointerType ProcessHeaps;
  75. PointerType GdiSharedHandleTable;
  76. };
  77. // Found from
  78. // https://stackoverflow.com/questions/13905661/how-to-get-list-of-gdi-handles.
  79. enum GdiHandleType : USHORT {
  80. kDC = 1,
  81. kRegion = 4,
  82. kBitmap = 5,
  83. kPalette = 8,
  84. kFont = 10,
  85. kBrush = 16,
  86. kPen = 48,
  87. };
  88. // Adapted from GDICELL.
  89. template <typename PointerType>
  90. struct GdiTableEntry {
  91. PointerType pKernelAddress;
  92. USHORT wProcessId;
  93. USHORT wCount;
  94. USHORT wUpper;
  95. GdiHandleType wType;
  96. PointerType pUserAddress;
  97. };
  98. // Types and names used for regular processes.
  99. struct RegularProcessTypes {
  100. using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
  101. static const char* query_information_process_name;
  102. // PROCESS_BASIC_INFORMATION
  103. struct ProcessBasicInformation {
  104. PVOID Reserved1;
  105. PVOID PebBaseAddress;
  106. PVOID Reserved2[2];
  107. ULONG_PTR UniqueProcessId;
  108. PVOID Reserved3;
  109. };
  110. using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
  111. IN PVOID BaseAddress,
  112. OUT PVOID Buffer,
  113. IN SIZE_T Size,
  114. OUT PSIZE_T NumberOfBytesRead);
  115. static const char* read_virtual_memory_func_name;
  116. using NativePointerType = PVOID;
  117. };
  118. // static
  119. const char* RegularProcessTypes::query_information_process_name =
  120. "NtQueryInformationProcess";
  121. // static
  122. const char* RegularProcessTypes::read_virtual_memory_func_name =
  123. "NtReadVirtualMemory";
  124. // Types and names used for WOW based processes.
  125. struct WowProcessTypes {
  126. // http://crbug.com/972185: Clang doesn't handle PVOID64 correctly, so we use
  127. // uint64_t as a substitute.
  128. // NtWow64QueryInformationProcess64 and NtQueryInformationProcess share the
  129. // same signature.
  130. using QueryInformationProcessFunc = decltype(NtQueryInformationProcess);
  131. static const char* query_information_process_name;
  132. // PROCESS_BASIC_INFORMATION_WOW64
  133. struct ProcessBasicInformation {
  134. PVOID Reserved1[2];
  135. uint64_t PebBaseAddress;
  136. PVOID Reserved2[4];
  137. ULONG_PTR UniqueProcessId[2];
  138. PVOID Reserved3[2];
  139. };
  140. using ReadVirtualMemoryFunc = NTSTATUS NTAPI(IN HANDLE ProcessHandle,
  141. IN uint64_t BaseAddress,
  142. OUT PVOID Buffer,
  143. IN ULONG64 Size,
  144. OUT PULONG64 NumberOfBytesRead);
  145. static const char* read_virtual_memory_func_name;
  146. using NativePointerType = uint64_t;
  147. };
  148. // static
  149. const char* WowProcessTypes::query_information_process_name =
  150. "NtWow64QueryInformationProcess64";
  151. // static
  152. const char* WowProcessTypes::read_virtual_memory_func_name =
  153. "NtWow64ReadVirtualMemory64";
  154. // To prevent from having to write a regular and WOW codepaths that do the same
  155. // thing with different structures and functions, GetGdiTableEntries is
  156. // templated to expect either RegularProcessTypes or WowProcessTypes.
  157. template <typename ProcessType>
  158. std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
  159. GetGdiTableEntries(const base::Process& process) {
  160. using GdiTableEntryVector =
  161. std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>;
  162. HMODULE ntdll = GetModuleHandle(L"ntdll.dll");
  163. if (!ntdll)
  164. return GdiTableEntryVector();
  165. static auto query_information_process_func =
  166. reinterpret_cast<typename ProcessType::QueryInformationProcessFunc*>(
  167. GetProcAddress(ntdll, ProcessType::query_information_process_name));
  168. if (!query_information_process_func) {
  169. LOG(ERROR) << ProcessType::query_information_process_name << " Missing";
  170. return GdiTableEntryVector();
  171. }
  172. typename ProcessType::ProcessBasicInformation basic_info;
  173. NTSTATUS result =
  174. query_information_process_func(process.Handle(), ProcessBasicInformation,
  175. &basic_info, sizeof(basic_info), nullptr);
  176. if (result != 0) {
  177. LOG(ERROR) << ProcessType::query_information_process_name << " Failed "
  178. << std::hex << result;
  179. return GdiTableEntryVector();
  180. }
  181. static auto read_virtual_mem_func =
  182. reinterpret_cast<typename ProcessType::ReadVirtualMemoryFunc*>(
  183. GetProcAddress(ntdll, ProcessType::read_virtual_memory_func_name));
  184. if (!read_virtual_mem_func) {
  185. LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " Missing";
  186. return GdiTableEntryVector();
  187. }
  188. PartialWinPeb<typename ProcessType::NativePointerType> peb;
  189. result = read_virtual_mem_func(process.Handle(), basic_info.PebBaseAddress,
  190. &peb, sizeof(peb), nullptr);
  191. if (result != 0) {
  192. LOG(ERROR) << ProcessType::read_virtual_memory_func_name << " PEB Failed "
  193. << std::hex << result;
  194. return GdiTableEntryVector();
  195. }
  196. // Estimated size derived from address space allocation of the table:
  197. // Windows 10
  198. // 32-bit Size: 1052672 bytes
  199. // 64-bit Size: 1576960 bytes
  200. // sizeof(GdiTableEntry)
  201. // 32-bit: 16 bytes
  202. // 64-bit: 24 bytes
  203. // Entry Count
  204. // 32-bit: 65792
  205. // 64-bit: 65706ish
  206. // So we'll take a look at 65536 entries since that's the maximum handle count.
  207. constexpr int kGdiTableEntryCount = 65536;
  208. GdiTableEntryVector entries;
  209. entries.resize(kGdiTableEntryCount);
  210. result = read_virtual_mem_func(
  211. process.Handle(), peb.GdiSharedHandleTable, &entries[0],
  212. sizeof(typename GdiTableEntryVector::value_type) * entries.size(),
  213. nullptr);
  214. if (result != 0) {
  215. LOG(ERROR) << ProcessType::read_virtual_memory_func_name
  216. << " GDI Handle Table Failed " << std::hex << result;
  217. return GdiTableEntryVector();
  218. }
  219. return entries;
  220. }
  221. // Iterates through |gdi_table| and finds handles that belong to |pid|,
  222. // incrementing the appropriate fields in |base::debug::GdiHandleCounts|.
  223. template <typename PointerType>
  224. base::debug::GdiHandleCounts CountHandleTypesFromTable(
  225. DWORD pid,
  226. const std::vector<GdiTableEntry<PointerType>>& gdi_table) {
  227. base::debug::GdiHandleCounts counts{};
  228. for (const auto& entry : gdi_table) {
  229. if (entry.wProcessId != pid)
  230. continue;
  231. switch (entry.wType & 0x7F) {
  232. case GdiHandleType::kDC:
  233. ++counts.dcs;
  234. break;
  235. case GdiHandleType::kRegion:
  236. ++counts.regions;
  237. break;
  238. case GdiHandleType::kBitmap:
  239. ++counts.bitmaps;
  240. break;
  241. case GdiHandleType::kPalette:
  242. ++counts.palettes;
  243. break;
  244. case GdiHandleType::kFont:
  245. ++counts.fonts;
  246. break;
  247. case GdiHandleType::kBrush:
  248. ++counts.brushes;
  249. break;
  250. case GdiHandleType::kPen:
  251. ++counts.pens;
  252. break;
  253. default:
  254. ++counts.unknown;
  255. break;
  256. }
  257. }
  258. counts.total_tracked = counts.dcs + counts.regions + counts.bitmaps +
  259. counts.palettes + counts.fonts + counts.brushes +
  260. counts.pens + counts.unknown;
  261. return counts;
  262. }
  263. template <typename ProcessType>
  264. absl::optional<base::debug::GdiHandleCounts> CollectGdiHandleCountsImpl(
  265. DWORD pid) {
  266. base::Process process = base::Process::OpenWithExtraPrivileges(pid);
  267. if (!process.IsValid())
  268. return absl::nullopt;
  269. std::vector<GdiTableEntry<typename ProcessType::NativePointerType>>
  270. gdi_entries = GetGdiTableEntries<ProcessType>(process);
  271. return CountHandleTypesFromTable(pid, gdi_entries);
  272. }
  273. // Returns the GDI Handle counts from the GDI Shared handle table. Empty on
  274. // failure.
  275. absl::optional<base::debug::GdiHandleCounts> CollectGdiHandleCounts(DWORD pid) {
  276. if (base::win::OSInfo::GetInstance()->IsWowX86OnAMD64()) {
  277. return CollectGdiHandleCountsImpl<WowProcessTypes>(pid);
  278. }
  279. return CollectGdiHandleCountsImpl<RegularProcessTypes>(pid);
  280. }
  281. constexpr size_t kLotsOfMemory = 1500 * 1024 * 1024; // 1.5GB
  282. HANDLE NOINLINE GetToolhelpSnapshot() {
  283. HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
  284. CHECK_NE(INVALID_HANDLE_VALUE, snapshot);
  285. return snapshot;
  286. }
  287. void NOINLINE GetFirstProcess(HANDLE snapshot, PROCESSENTRY32* proc_entry) {
  288. proc_entry->dwSize = sizeof(PROCESSENTRY32);
  289. CHECK(Process32First(snapshot, proc_entry));
  290. }
  291. void NOINLINE CrashIfExcessiveHandles(DWORD num_gdi_handles) {
  292. // By default, Windows 10 allows a max of 10,000 GDI handles per process.
  293. // Number found by inspecting
  294. //
  295. // HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\
  296. // CurrentVersion\Windows\GDIProcessHandleQuota
  297. //
  298. // on a Windows 10 laptop.
  299. static constexpr DWORD kLotsOfHandles = 9990;
  300. CHECK_LE(num_gdi_handles, kLotsOfHandles);
  301. }
  302. void NOINLINE
  303. CrashIfPagefileUsageTooLarge(const PROCESS_MEMORY_COUNTERS_EX& pmc) {
  304. CHECK_LE(pmc.PagefileUsage, kLotsOfMemory);
  305. }
  306. void NOINLINE
  307. CrashIfPrivateUsageTooLarge(const PROCESS_MEMORY_COUNTERS_EX& pmc) {
  308. CHECK_LE(pmc.PrivateUsage, kLotsOfMemory);
  309. }
  310. void NOINLINE CrashIfCannotAllocateSmallBitmap(BITMAPINFOHEADER* header,
  311. HANDLE shared_section) {
  312. void* small_data = nullptr;
  313. base::debug::Alias(&small_data);
  314. header->biWidth = 5;
  315. header->biHeight = -5;
  316. HBITMAP small_bitmap =
  317. CreateDIBSection(nullptr, reinterpret_cast<BITMAPINFO*>(&header), 0,
  318. &small_data, shared_section, 0);
  319. CHECK(small_bitmap != nullptr);
  320. DeleteObject(small_bitmap);
  321. }
  322. void NOINLINE GetProcessMemoryInfo(PROCESS_MEMORY_COUNTERS_EX* pmc) {
  323. pmc->cb = sizeof(*pmc);
  324. CHECK(GetProcessMemoryInfo(GetCurrentProcess(),
  325. reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(pmc),
  326. sizeof(*pmc)));
  327. }
  328. DWORD NOINLINE GetNumGdiHandles() {
  329. DWORD num_gdi_handles = GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS);
  330. if (num_gdi_handles == 0) {
  331. DWORD get_gui_resources_error = GetLastError();
  332. base::debug::Alias(&get_gui_resources_error);
  333. CHECK(false);
  334. }
  335. return num_gdi_handles;
  336. }
  337. void CollectChildGDIUsageAndDie(DWORD parent_pid) {
  338. HANDLE snapshot = GetToolhelpSnapshot();
  339. int total_process_count = 0;
  340. base::debug::Alias(&total_process_count);
  341. DWORD total_peak_gdi_count = 0;
  342. base::debug::Alias(&total_peak_gdi_count);
  343. DWORD total_gdi_count = 0;
  344. base::debug::Alias(&total_gdi_count);
  345. DWORD total_user_count = 0;
  346. base::debug::Alias(&total_user_count);
  347. int child_count = 0;
  348. base::debug::Alias(&child_count);
  349. DWORD peak_gdi_count = 0;
  350. base::debug::Alias(&peak_gdi_count);
  351. DWORD sum_gdi_count = 0;
  352. base::debug::Alias(&sum_gdi_count);
  353. DWORD sum_user_count = 0;
  354. base::debug::Alias(&sum_user_count);
  355. PROCESSENTRY32 proc_entry = {};
  356. GetFirstProcess(snapshot, &proc_entry);
  357. do {
  358. base::win::ScopedHandle process(
  359. OpenProcess(PROCESS_QUERY_INFORMATION,
  360. FALSE,
  361. proc_entry.th32ProcessID));
  362. if (!process.is_valid())
  363. continue;
  364. DWORD num_gdi_handles = GetGuiResources(process.get(), GR_GDIOBJECTS);
  365. DWORD num_user_handles = GetGuiResources(process.get(), GR_USEROBJECTS);
  366. // Compute sum and peak counts for all processes.
  367. ++total_process_count;
  368. total_user_count += num_user_handles;
  369. total_gdi_count += num_gdi_handles;
  370. total_peak_gdi_count = std::max(total_peak_gdi_count, num_gdi_handles);
  371. if (parent_pid != proc_entry.th32ParentProcessID)
  372. continue;
  373. // Compute sum and peak counts for child processes.
  374. ++child_count;
  375. sum_user_count += num_user_handles;
  376. sum_gdi_count += num_gdi_handles;
  377. peak_gdi_count = std::max(peak_gdi_count, num_gdi_handles);
  378. } while (Process32Next(snapshot, &proc_entry));
  379. CloseHandle(snapshot);
  380. CHECK(false);
  381. }
  382. } // namespace
  383. namespace base {
  384. namespace debug {
  385. void CollectGDIUsageAndDie(BITMAPINFOHEADER* header, HANDLE shared_section) {
  386. // Make sure parameters are saved in the minidump.
  387. DWORD last_error = GetLastError();
  388. bool is_gdi_available = base::win::IsUser32AndGdi32Available();
  389. LONG width = header ? header->biWidth : 0;
  390. LONG height = header ? header->biHeight : 0;
  391. base::debug::Alias(&last_error);
  392. base::debug::Alias(&is_gdi_available);
  393. base::debug::Alias(&width);
  394. base::debug::Alias(&height);
  395. base::debug::Alias(&shared_section);
  396. DWORD num_user_handles = GetGuiResources(GetCurrentProcess(), GR_USEROBJECTS);
  397. DWORD num_gdi_handles = GetNumGdiHandles();
  398. DWORD peak_gdi_handles =
  399. GetGuiResources(GetCurrentProcess(), GR_GDIOBJECTS_PEAK);
  400. DWORD num_global_gdi_handles = GetGuiResources(GR_GLOBAL, GR_GDIOBJECTS);
  401. DWORD num_global_user_handles = GetGuiResources(GR_GLOBAL, GR_USEROBJECTS);
  402. base::debug::Alias(&num_gdi_handles);
  403. base::debug::Alias(&num_user_handles);
  404. base::debug::Alias(&peak_gdi_handles);
  405. base::debug::Alias(&num_global_gdi_handles);
  406. base::debug::Alias(&num_global_user_handles);
  407. absl::optional<GdiHandleCounts> optional_handle_counts =
  408. CollectGdiHandleCounts(GetCurrentProcessId());
  409. bool handle_counts_set = optional_handle_counts.has_value();
  410. GdiHandleCounts handle_counts =
  411. optional_handle_counts.value_or(GdiHandleCounts());
  412. int tracked_dcs = handle_counts.dcs;
  413. int tracked_regions = handle_counts.regions;
  414. int tracked_bitmaps = handle_counts.bitmaps;
  415. int tracked_palettes = handle_counts.palettes;
  416. int tracked_fonts = handle_counts.fonts;
  417. int tracked_brushes = handle_counts.brushes;
  418. int tracked_pens = handle_counts.pens;
  419. int tracked_unknown_handles = handle_counts.unknown;
  420. int tracked_total = handle_counts.total_tracked;
  421. base::debug::Alias(&handle_counts_set);
  422. base::debug::Alias(&tracked_dcs);
  423. base::debug::Alias(&tracked_regions);
  424. base::debug::Alias(&tracked_bitmaps);
  425. base::debug::Alias(&tracked_palettes);
  426. base::debug::Alias(&tracked_fonts);
  427. base::debug::Alias(&tracked_brushes);
  428. base::debug::Alias(&tracked_pens);
  429. base::debug::Alias(&tracked_unknown_handles);
  430. base::debug::Alias(&tracked_total);
  431. CrashIfExcessiveHandles(num_gdi_handles);
  432. PROCESS_MEMORY_COUNTERS_EX pmc;
  433. GetProcessMemoryInfo(&pmc);
  434. CrashIfPagefileUsageTooLarge(pmc);
  435. CrashIfPrivateUsageTooLarge(pmc);
  436. if (std::abs(height) * width > 100) {
  437. // Huh, that's weird. We don't have crazy handle count, we don't have
  438. // ridiculous memory usage. Try to allocate a small bitmap and see if that
  439. // fails too.
  440. CrashIfCannotAllocateSmallBitmap(header, shared_section);
  441. }
  442. // Maybe the child processes are the ones leaking GDI or USER resouces.
  443. CollectChildGDIUsageAndDie(GetCurrentProcessId());
  444. }
  445. GdiHandleCounts GetGDIHandleCountsInCurrentProcessForTesting() {
  446. absl::optional<GdiHandleCounts> handle_counts =
  447. CollectGdiHandleCounts(GetCurrentProcessId());
  448. DCHECK(handle_counts.has_value());
  449. return handle_counts.value_or(GdiHandleCounts());
  450. }
  451. } // namespace debug
  452. } // namespace base