• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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/win/windows_version.h"
6 
7 #include <windows.h>
8 
9 #include <memory>
10 #include <tuple>
11 #include <utility>
12 
13 #include "base/check_op.h"
14 #include "base/debug/crash_logging.h"
15 #include "base/debug/dump_without_crashing.h"
16 #include "base/file_version_info_win.h"
17 #include "base/files/file_path.h"
18 #include "base/logging.h"
19 #include "base/no_destructor.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/threading/thread_restrictions.h"
23 #include "base/win/registry.h"
24 #include "build/build_config.h"
25 
26 #if !defined(__clang__) && _MSC_FULL_VER < 191125507
27 #error VS 2017 Update 3.2 or higher is required
28 #endif
29 
30 #if !defined(NTDDI_WIN10_NI)
31 #error Windows 10.0.22621.0 SDK or higher required.
32 #endif
33 
34 namespace base {
35 namespace win {
36 
37 namespace {
38 
39 // The values under the CurrentVersion registry hive are mirrored under
40 // the corresponding Wow6432 hive.
41 constexpr wchar_t kRegKeyWindowsNTCurrentVersion[] =
42     L"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion";
43 
44 // Returns the "UBR" (Windows 10 patch number) and "DisplayVersion" (or
45 // "ReleaseId" on earlier versions) (Windows 10 release number) from registry.
46 // "UBR" is an undocumented value and will be 0 if the value was not found.
47 // "ReleaseId" will be an empty string if neither new nor old values are found.
GetVersionData()48 std::pair<int, std::string> GetVersionData() {
49   DWORD ubr = 0;
50   std::wstring release_id;
51   RegKey key;
52 
53   if (key.Open(HKEY_LOCAL_MACHINE, kRegKeyWindowsNTCurrentVersion,
54                KEY_QUERY_VALUE) == ERROR_SUCCESS) {
55     key.ReadValueDW(L"UBR", &ubr);
56     // "DisplayVersion" has been introduced in Windows 10 2009
57     // when naming changed to mixed letters and numbers.
58     key.ReadValue(L"DisplayVersion", &release_id);
59     // Use discontinued "ReleaseId" instead, if the former is unavailable.
60     if (release_id.empty())
61       key.ReadValue(L"ReleaseId", &release_id);
62   }
63 
64   return std::make_pair(static_cast<int>(ubr), WideToUTF8(release_id));
65 }
66 
GetSystemInfoStorage()67 const _SYSTEM_INFO& GetSystemInfoStorage() {
68   static const _SYSTEM_INFO system_info = [] {
69     _SYSTEM_INFO info = {};
70     ::GetNativeSystemInfo(&info);
71     return info;
72   }();
73   return system_info;
74 }
75 
76 }  // namespace
77 
78 // static
GetInstanceStorage()79 OSInfo** OSInfo::GetInstanceStorage() {
80   // Note: we don't use the Singleton class because it depends on AtExitManager,
81   // and it's convenient for other modules to use this class without it.
82   static OSInfo* info = [] {
83     _OSVERSIONINFOEXW version_info = {sizeof(version_info)};
84 
85 #pragma clang diagnostic push
86 #pragma clang diagnostic ignored "-Wdeprecated-declarations"
87     // GetVersionEx() is deprecated, and the suggested replacement are
88     // the IsWindows*OrGreater() functions in VersionHelpers.h. We can't
89     // use that because:
90     // - For Windows 10, there's IsWindows10OrGreater(), but nothing more
91     //   granular. We need to be able to detect different Windows 10 releases
92     //   since they sometimes change behavior in ways that matter.
93     // - There is no IsWindows11OrGreater() function yet.
94     ::GetVersionEx(reinterpret_cast<_OSVERSIONINFOW*>(&version_info));
95 #pragma clang diagnostic pop
96 
97     DWORD os_type = 0;
98     ::GetProductInfo(version_info.dwMajorVersion, version_info.dwMinorVersion,
99                      0, 0, &os_type);
100 
101     return new OSInfo(version_info, GetSystemInfoStorage(), os_type);
102   }();
103 
104   return &info;
105 }
106 
107 // static
GetInstance()108 OSInfo* OSInfo::GetInstance() {
109   return *GetInstanceStorage();
110 }
111 
112 // static
GetArchitecture()113 OSInfo::WindowsArchitecture OSInfo::GetArchitecture() {
114   switch (GetSystemInfoStorage().wProcessorArchitecture) {
115     case PROCESSOR_ARCHITECTURE_INTEL:
116       return X86_ARCHITECTURE;
117     case PROCESSOR_ARCHITECTURE_AMD64:
118       return X64_ARCHITECTURE;
119     case PROCESSOR_ARCHITECTURE_IA64:
120       return IA64_ARCHITECTURE;
121     case PROCESSOR_ARCHITECTURE_ARM64:
122       return ARM64_ARCHITECTURE;
123     default:
124       return OTHER_ARCHITECTURE;
125   }
126 }
127 
128 // Returns true if this is an x86/x64 process running on ARM64 through
129 // emulation.
130 // static
IsRunningEmulatedOnArm64()131 bool OSInfo::IsRunningEmulatedOnArm64() {
132 #if defined(ARCH_CPU_ARM64)
133   // If we're running native ARM64 then we aren't running emulated.
134   return false;
135 #else
136   using IsWow64Process2Function = decltype(&IsWow64Process2);
137 
138   IsWow64Process2Function is_wow64_process2 =
139       reinterpret_cast<IsWow64Process2Function>(::GetProcAddress(
140           ::GetModuleHandleA("kernel32.dll"), "IsWow64Process2"));
141   if (!is_wow64_process2) {
142     return false;
143   }
144   USHORT process_machine;
145   USHORT native_machine;
146   bool retval = is_wow64_process2(::GetCurrentProcess(), &process_machine,
147                                   &native_machine);
148   if (!retval) {
149     return false;
150   }
151   if (native_machine == IMAGE_FILE_MACHINE_ARM64) {
152     return true;
153   }
154   return false;
155 #endif
156 }
157 
OSInfo(const _OSVERSIONINFOEXW & version_info,const _SYSTEM_INFO & system_info,DWORD os_type)158 OSInfo::OSInfo(const _OSVERSIONINFOEXW& version_info,
159                const _SYSTEM_INFO& system_info,
160                DWORD os_type)
161     : version_(Version::PRE_XP),
162       wow_process_machine_(WowProcessMachine::kUnknown),
163       wow_native_machine_(WowNativeMachine::kUnknown),
164       os_type_(os_type) {
165   version_number_.major = version_info.dwMajorVersion;
166   version_number_.minor = version_info.dwMinorVersion;
167   version_number_.build = version_info.dwBuildNumber;
168   std::tie(version_number_.patch, release_id_) = GetVersionData();
169   version_ = MajorMinorBuildToVersion(
170       version_number_.major, version_number_.minor, version_number_.build);
171   InitializeWowStatusValuesForProcess(GetCurrentProcess());
172   service_pack_.major = version_info.wServicePackMajor;
173   service_pack_.minor = version_info.wServicePackMinor;
174   service_pack_str_ = WideToUTF8(version_info.szCSDVersion);
175 
176   processors_ = static_cast<int>(system_info.dwNumberOfProcessors);
177   allocation_granularity_ = system_info.dwAllocationGranularity;
178 
179   if (version_info.dwMajorVersion == 6 || version_info.dwMajorVersion == 10) {
180     // Only present on Vista+.
181     switch (os_type) {
182       case PRODUCT_CLUSTER_SERVER:
183       case PRODUCT_DATACENTER_SERVER:
184       case PRODUCT_DATACENTER_SERVER_CORE:
185       case PRODUCT_ENTERPRISE_SERVER:
186       case PRODUCT_ENTERPRISE_SERVER_CORE:
187       case PRODUCT_ENTERPRISE_SERVER_IA64:
188       case PRODUCT_SMALLBUSINESS_SERVER:
189       case PRODUCT_SMALLBUSINESS_SERVER_PREMIUM:
190       case PRODUCT_STANDARD_SERVER:
191       case PRODUCT_STANDARD_SERVER_CORE:
192       case PRODUCT_WEB_SERVER:
193         version_type_ = SUITE_SERVER;
194         break;
195       case PRODUCT_PROFESSIONAL:
196       case PRODUCT_ULTIMATE:
197         version_type_ = SUITE_PROFESSIONAL;
198         break;
199       case PRODUCT_ENTERPRISE:
200       case PRODUCT_ENTERPRISE_E:
201       case PRODUCT_ENTERPRISE_EVALUATION:
202       case PRODUCT_ENTERPRISE_N:
203       case PRODUCT_ENTERPRISE_N_EVALUATION:
204       case PRODUCT_ENTERPRISE_S:
205       case PRODUCT_ENTERPRISE_S_EVALUATION:
206       case PRODUCT_ENTERPRISE_S_N:
207       case PRODUCT_ENTERPRISE_S_N_EVALUATION:
208       case PRODUCT_ENTERPRISE_SUBSCRIPTION:
209       case PRODUCT_ENTERPRISE_SUBSCRIPTION_N:
210       case PRODUCT_BUSINESS:
211       case PRODUCT_BUSINESS_N:
212       case PRODUCT_IOTENTERPRISE:
213       case PRODUCT_IOTENTERPRISES:
214         version_type_ = SUITE_ENTERPRISE;
215         break;
216       case PRODUCT_PRO_FOR_EDUCATION:
217       case PRODUCT_PRO_FOR_EDUCATION_N:
218         version_type_ = SUITE_EDUCATION_PRO;
219         break;
220       case PRODUCT_EDUCATION:
221       case PRODUCT_EDUCATION_N:
222         version_type_ = SUITE_EDUCATION;
223         break;
224       case PRODUCT_HOME_BASIC:
225       case PRODUCT_HOME_PREMIUM:
226       case PRODUCT_STARTER:
227       default:
228         version_type_ = SUITE_HOME;
229         break;
230     }
231   } else if (version_info.dwMajorVersion == 5 &&
232              version_info.dwMinorVersion == 2) {
233     if (version_info.wProductType == VER_NT_WORKSTATION &&
234         system_info.wProcessorArchitecture == PROCESSOR_ARCHITECTURE_AMD64) {
235       version_type_ = SUITE_PROFESSIONAL;
236     } else if (version_info.wSuiteMask & VER_SUITE_WH_SERVER) {
237       version_type_ = SUITE_HOME;
238     } else {
239       version_type_ = SUITE_SERVER;
240     }
241   } else if (version_info.dwMajorVersion == 5 &&
242              version_info.dwMinorVersion == 1) {
243     if (version_info.wSuiteMask & VER_SUITE_PERSONAL)
244       version_type_ = SUITE_HOME;
245     else
246       version_type_ = SUITE_PROFESSIONAL;
247   } else {
248     // Windows is pre XP so we don't care but pick a safe default.
249     version_type_ = SUITE_HOME;
250   }
251 }
252 
253 OSInfo::~OSInfo() = default;
254 
Kernel32Version()255 Version OSInfo::Kernel32Version() {
256   static const Version kernel32_version =
257       MajorMinorBuildToVersion(Kernel32BaseVersion().components()[0],
258                                Kernel32BaseVersion().components()[1],
259                                Kernel32BaseVersion().components()[2]);
260   return kernel32_version;
261 }
262 
Kernel32VersionNumber()263 OSInfo::VersionNumber OSInfo::Kernel32VersionNumber() {
264   DCHECK_EQ(Kernel32BaseVersion().components().size(), 4u);
265   static const VersionNumber version = {
266       .major = Kernel32BaseVersion().components()[0],
267       .minor = Kernel32BaseVersion().components()[1],
268       .build = Kernel32BaseVersion().components()[2],
269       .patch = Kernel32BaseVersion().components()[3]};
270   return version;
271 }
272 
273 // Retrieve a version from kernel32. This is useful because when running in
274 // compatibility mode for a down-level version of the OS, the file version of
275 // kernel32 will still be the "real" version.
Kernel32BaseVersion()276 base::Version OSInfo::Kernel32BaseVersion() {
277   static const NoDestructor<base::Version> version([] {
278     // Allow the calls to `Kernel32BaseVersion()` to block, as they only happen
279     // once (after which the result is cached in `version`), and reading from
280     // kernel32.dll is fast in practice because it is used by all processes and
281     // therefore likely to be in the OS's file cache.
282     base::ScopedAllowBlocking allow_blocking;
283     std::unique_ptr<FileVersionInfoWin> file_version_info =
284         FileVersionInfoWin::CreateFileVersionInfoWin(
285             FilePath(FILE_PATH_LITERAL("kernel32.dll")));
286     if (!file_version_info) {
287       // crbug.com/912061: on some systems it seems kernel32.dll might be
288       // corrupted or not in a state to get version info. In this case try
289       // kernelbase.dll as a fallback.
290       file_version_info = FileVersionInfoWin::CreateFileVersionInfoWin(
291           FilePath(FILE_PATH_LITERAL("kernelbase.dll")));
292     }
293     CHECK(file_version_info);
294     return file_version_info->GetFileVersion();
295   }());
296   return *version;
297 }
298 
IsWowDisabled() const299 bool OSInfo::IsWowDisabled() const {
300   return (wow_process_machine_ == WowProcessMachine::kDisabled);
301 }
302 
IsWowX86OnAMD64() const303 bool OSInfo::IsWowX86OnAMD64() const {
304   return (wow_process_machine_ == WowProcessMachine::kX86 &&
305           wow_native_machine_ == WowNativeMachine::kAMD64);
306 }
307 
IsWowX86OnARM64() const308 bool OSInfo::IsWowX86OnARM64() const {
309   return (wow_process_machine_ == WowProcessMachine::kX86 &&
310           wow_native_machine_ == WowNativeMachine::kARM64);
311 }
312 
IsWowAMD64OnARM64() const313 bool OSInfo::IsWowAMD64OnARM64() const {
314 #if defined(ARCH_CPU_X86_64)
315   // An AMD64 process running on an ARM64 device results in the incorrect
316   // identification of the device architecture (AMD64 is reported). However,
317   // IsWow64Process2 will return the correct device type for the native
318   // machine, even though the OS doesn't consider an AMD64 process on an ARM64
319   // processor a classic Windows-on-Windows setup.
320   return (wow_process_machine_ == WowProcessMachine::kDisabled &&
321           wow_native_machine_ == WowNativeMachine::kARM64);
322 #else
323   return false;
324 #endif
325 }
326 
IsWowX86OnOther() const327 bool OSInfo::IsWowX86OnOther() const {
328   return (wow_process_machine_ == WowProcessMachine::kX86 &&
329           wow_native_machine_ == WowNativeMachine::kOther);
330 }
331 
processor_model_name()332 std::string OSInfo::processor_model_name() {
333   if (processor_model_name_.empty()) {
334     const wchar_t kProcessorNameString[] =
335         L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0";
336     RegKey key(HKEY_LOCAL_MACHINE, kProcessorNameString, KEY_READ);
337     std::wstring value;
338     key.ReadValue(L"ProcessorNameString", &value);
339     processor_model_name_ = WideToUTF8(value);
340   }
341   return processor_model_name_;
342 }
343 
IsWindowsNSku() const344 bool OSInfo::IsWindowsNSku() const {
345   switch (os_type_) {
346     case PRODUCT_BUSINESS_N:
347     case PRODUCT_CORE_N:
348     case PRODUCT_CORE_CONNECTED_N:
349     case PRODUCT_EDUCATION_N:
350     case PRODUCT_ENTERPRISE_N:
351     case PRODUCT_ENTERPRISE_S_N:
352     case PRODUCT_ENTERPRISE_SUBSCRIPTION_N:
353     case PRODUCT_HOME_BASIC_N:
354     case PRODUCT_HOME_PREMIUM_N:
355     case PRODUCT_PRO_FOR_EDUCATION_N:
356     case PRODUCT_PRO_WORKSTATION_N:
357     case PRODUCT_PROFESSIONAL_N:
358     case PRODUCT_PROFESSIONAL_S_N:
359     case PRODUCT_PROFESSIONAL_STUDENT_N:
360     case PRODUCT_STARTER_N:
361     case PRODUCT_ULTIMATE_N:
362       return true;
363     default:
364       return false;
365   }
366 }
367 
368 // With the exception of Server 2003, server variants are treated the same as
369 // the corresponding workstation release.
370 // static
MajorMinorBuildToVersion(uint32_t major,uint32_t minor,uint32_t build)371 Version OSInfo::MajorMinorBuildToVersion(uint32_t major,
372                                          uint32_t minor,
373                                          uint32_t build) {
374   if (major == 11) {
375     // We know nothing about this version of Windows or even if it exists.
376     // Known Windows 11 versions have a major number 10 and are thus handled by
377     // the == 10 block below.
378     return Version::WIN11;
379   }
380 
381   if (major == 10) {
382     if (build >= 26100) {
383       return Version::WIN11_24H2;
384     }
385     if (build >= 22631) {
386       return Version::WIN11_23H2;
387     }
388     if (build >= 22621) {
389       return Version::WIN11_22H2;
390     }
391     if (build >= 22000) {
392       return Version::WIN11;
393     }
394     if (build >= 20348) {
395       return Version::SERVER_2022;
396     }
397     if (build >= 19045) {
398       return Version::WIN10_22H2;
399     }
400     if (build >= 19044) {
401       return Version::WIN10_21H2;
402     }
403     if (build >= 19043) {
404       return Version::WIN10_21H1;
405     }
406     if (build >= 19042) {
407       return Version::WIN10_20H2;
408     }
409     if (build >= 19041) {
410       return Version::WIN10_20H1;
411     }
412     if (build >= 18363) {
413       return Version::WIN10_19H2;
414     }
415     if (build >= 18362) {
416       return Version::WIN10_19H1;
417     }
418     if (build >= 17763) {
419       return Version::WIN10_RS5;
420     }
421     if (build >= 17134) {
422       return Version::WIN10_RS4;
423     }
424     if (build >= 16299) {
425       return Version::WIN10_RS3;
426     }
427     if (build >= 15063) {
428       return Version::WIN10_RS2;
429     }
430     if (build >= 14393) {
431       return Version::WIN10_RS1;
432     }
433     if (build >= 10586) {
434       return Version::WIN10_TH2;
435     }
436     return Version::WIN10;
437   }
438 
439   if (major > 6) {
440     // Hitting this likely means that it's time for a >11 block above.
441     LOG(DFATAL) << "Unsupported version: " << major << "." << minor << "."
442                 << build;
443 
444     SCOPED_CRASH_KEY_NUMBER("WindowsVersion", "major", major);
445     SCOPED_CRASH_KEY_NUMBER("WindowsVersion", "minor", minor);
446     SCOPED_CRASH_KEY_NUMBER("WindowsVersion", "build", build);
447     base::debug::DumpWithoutCrashing();
448 
449     return Version::WIN_LAST;
450   }
451 
452   if (major == 6) {
453     switch (minor) {
454       case 0:
455         return Version::VISTA;
456       case 1:
457         return Version::WIN7;
458       case 2:
459         return Version::WIN8;
460       default:
461         DCHECK_EQ(minor, 3u);
462         return Version::WIN8_1;
463     }
464   }
465 
466   if (major == 5 && minor != 0) {
467     // Treat XP Pro x64, Home Server, and Server 2003 R2 as Server 2003.
468     return minor == 1 ? Version::XP : Version::SERVER_2003;
469   }
470 
471   // Win 2000 or older.
472   return Version::PRE_XP;
473 }
474 
GetVersion()475 Version GetVersion() {
476   return OSInfo::GetInstance()->version();
477 }
478 
GetWowProcessMachineArchitecture(const int process_machine)479 OSInfo::WowProcessMachine OSInfo::GetWowProcessMachineArchitecture(
480     const int process_machine) {
481   switch (process_machine) {
482     case IMAGE_FILE_MACHINE_UNKNOWN:
483       return OSInfo::WowProcessMachine::kDisabled;
484     case IMAGE_FILE_MACHINE_I386:
485       return OSInfo::WowProcessMachine::kX86;
486     case IMAGE_FILE_MACHINE_ARM:
487     case IMAGE_FILE_MACHINE_THUMB:
488     case IMAGE_FILE_MACHINE_ARMNT:
489       return OSInfo::WowProcessMachine::kARM32;
490   }
491   return OSInfo::WowProcessMachine::kOther;
492 }
493 
GetWowNativeMachineArchitecture(const int native_machine)494 OSInfo::WowNativeMachine OSInfo::GetWowNativeMachineArchitecture(
495     const int native_machine) {
496   switch (native_machine) {
497     case IMAGE_FILE_MACHINE_ARM64:
498       return OSInfo::WowNativeMachine::kARM64;
499     case IMAGE_FILE_MACHINE_AMD64:
500       return OSInfo::WowNativeMachine::kAMD64;
501   }
502   return OSInfo::WowNativeMachine::kOther;
503 }
504 
InitializeWowStatusValuesFromLegacyApi(HANDLE process_handle)505 void OSInfo::InitializeWowStatusValuesFromLegacyApi(HANDLE process_handle) {
506   BOOL is_wow64 = FALSE;
507   if (!::IsWow64Process(process_handle, &is_wow64))
508     return;
509   if (is_wow64) {
510     wow_process_machine_ = WowProcessMachine::kX86;
511     wow_native_machine_ = WowNativeMachine::kAMD64;
512   } else {
513     wow_process_machine_ = WowProcessMachine::kDisabled;
514   }
515 }
516 
InitializeWowStatusValuesForProcess(HANDLE process_handle)517 void OSInfo::InitializeWowStatusValuesForProcess(HANDLE process_handle) {
518   static const auto is_wow64_process2 =
519       reinterpret_cast<decltype(&IsWow64Process2)>(::GetProcAddress(
520           ::GetModuleHandle(L"kernel32.dll"), "IsWow64Process2"));
521   if (!is_wow64_process2) {
522     InitializeWowStatusValuesFromLegacyApi(process_handle);
523     return;
524   }
525 
526   USHORT process_machine = IMAGE_FILE_MACHINE_UNKNOWN;
527   USHORT native_machine = IMAGE_FILE_MACHINE_UNKNOWN;
528   if (!is_wow64_process2(process_handle, &process_machine, &native_machine)) {
529     return;
530   }
531   wow_process_machine_ = GetWowProcessMachineArchitecture(process_machine);
532   wow_native_machine_ = GetWowNativeMachineArchitecture(native_machine);
533 }
534 
535 }  // namespace win
536 }  // namespace base
537