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