]> git.ipfire.org Git - thirdparty/pciutils.git/commitdiff
windows: Improve win32_change_error_mode()
authorPali Rohár <pali@kernel.org>
Thu, 21 Nov 2024 23:24:51 +0000 (00:24 +0100)
committerMartin Mares <mj@ucw.cz>
Sun, 8 Jun 2025 15:19:58 +0000 (17:19 +0200)
Attempt to do two other thread safe methods before doing fallback to thread
unsafe SetErrorMode() method.

lib/win32-helpers.c

index 3d514a2c95eec92fde2fa45fbf71eab726883f8f..18c9f4a13b7886a4bdf135df1af7d1d5f951d7a7 100644 (file)
 #define SE_SACL_AUTO_INHERITED 0x0800
 #endif
 
+/* Older SDK versions do not provide NtCurrentTeb symbol for X86, header files have only function declaration. */
+#if defined(_MSC_VER) && defined(_M_IX86) && !defined(PcTeb)
+#define PcTeb 0x18
+#if _MSC_VER >= 1400
+#pragma intrinsic(__readfsdword)
+__inline struct _TEB *NtCurrentTeb(void) { return (struct _TEB *)__readfsdword(PcTeb); }
+#else
+__inline struct _TEB *NtCurrentTeb(void) { __asm mov eax, fs:[PcTeb] }
+#endif
+#endif
+
+/* Offset to ULONG HardErrorMode field in TEB structure, it is architecture specific. */
+#if defined(_M_IX86) || defined(__i386__)
+#define TEB_HARD_ERROR_MODE_OFFSET 0x0F28
+#elif defined(_M_AMD64) || defined(__x86_64__)
+#define TEB_HARD_ERROR_MODE_OFFSET 0x16B0
+#endif
+
 /*
  * These psapi functions are available in kernel32.dll library with K32 prefix
  * on Windows 7 and higher systems. On older Windows systems these functions are
@@ -168,7 +186,9 @@ UINT
 win32_change_error_mode(UINT new_mode)
 {
   SetThreadErrorModeProt MySetThreadErrorMode = NULL;
+  OSVERSIONINFOA version;
   HMODULE kernel32;
+  HMODULE ntdll;
   DWORD old_mode;
 
   /*
@@ -179,10 +199,38 @@ win32_change_error_mode(UINT new_mode)
   if (kernel32)
     MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(kernel32, "SetThreadErrorMode");
 
+  /*
+   * Function RtlSetThreadErrorMode() was introduced in Windows XP x64
+   * and Windows Server 2003. Use GetProcAddress() as it is in ntdll.dll.
+   */
+  if (!MySetThreadErrorMode)
+    {
+      ntdll = GetModuleHandle(TEXT("ntdll.dll"));
+      if (ntdll)
+        MySetThreadErrorMode = (SetThreadErrorModeProt)(void(*)(void))GetProcAddress(ntdll, "RtlSetThreadErrorMode");
+    }
+
   if (MySetThreadErrorMode &&
       MySetThreadErrorMode(new_mode, &old_mode))
     return old_mode;
 
+#ifdef TEB_HARD_ERROR_MODE_OFFSET
+  /*
+   * On Windows NT 4.0+ systems fallback to thread HardErrorMode API.
+   * It depends on architecture specific offset for HardErrorMode field in TEB.
+   */
+  version.dwOSVersionInfoSize = sizeof(version);
+  if (GetVersionExA(&version) &&
+      version.dwPlatformId == VER_PLATFORM_WIN32_NT &&
+      version.dwMajorVersion >= 4)
+    {
+      ULONG *hard_error_mode_ptr = (ULONG *)((BYTE *)NtCurrentTeb() + TEB_HARD_ERROR_MODE_OFFSET);
+      old_mode = *hard_error_mode_ptr;
+      *hard_error_mode_ptr = new_mode;
+      return old_mode;
+    }
+#endif
+
   /*
    * Fallback to function SetErrorMode() which modifies error mode of the
    * whole process and returns old mode.