Allow ASLR to be disabled on Windows
authorTom Tromey <tromey@adacore.com>
Wed, 8 Sep 2021 14:20:29 +0000 (08:20 -0600)
committerTom Tromey <tromey@adacore.com>
Tue, 7 Jun 2022 15:59:41 +0000 (09:59 -0600)
On Windows, it is possible to disable ASLR when creating a process.
This patch adds code to do this, and hooks it up to gdb's existing
disable-randomization feature.  Because the Windows documentation
cautions that this isn't available on all versions of Windows, the
CreateProcess wrapper function is updated to make the attempt, and
then fall back to the current approach if it fails.

gdb/NEWS
gdb/nat/windows-nat.c
gdb/nat/windows-nat.h
gdb/windows-nat.c
gdbserver/win32-low.cc
gdbserver/win32-low.h

index a85f59f56df5a12e77c13a7bd7ed8e1d18330833..7164e18bfa5585f54d02b5f6cdf4453e0de746a9 100644 (file)
--- a/gdb/NEWS
+++ b/gdb/NEWS
@@ -71,6 +71,8 @@ maintenance info line-table
   Python 2.  From GDB 13, it will only be possible to build GDB itself
   with Python 3 support.
 
+* The disable-randomization setting now works on Windows.
+
 * Improved C++ template support
 
   GDB now treats functions/types involving C++ templates like it does function
index 8048344752baa09dad0fe8e7d868a8201c2b2e40..e52da16d9cebf0c9195303e7203448a74f385f03 100644 (file)
@@ -69,6 +69,10 @@ GenerateConsoleCtrlEvent_ftype *GenerateConsoleCtrlEvent;
 typedef HRESULT WINAPI (GetThreadDescription_ftype) (HANDLE, PWSTR *);
 static GetThreadDescription_ftype *GetThreadDescription;
 
+InitializeProcThreadAttributeList_ftype *InitializeProcThreadAttributeList;
+UpdateProcThreadAttribute_ftype *UpdateProcThreadAttribute;
+DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList;
+
 /* Note that 'debug_events' must be locally defined in the relevant
    functions.  */
 #define DEBUG_EVENTS(fmt, ...) \
@@ -741,15 +745,104 @@ wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout)
   return result;
 }
 
-/* Helper template for the CreateProcess wrappers.  */
+/* Flags to pass to UpdateProcThreadAttribute.  */
+#define relocate_aslr_flags ((0x2 << 8) | (0x2 << 16))
+
+/* Attribute to pass to UpdateProcThreadAttribute.  */
+#define mitigation_policy 0x00020007
+
+/* Pick one of the symbols as a sentinel.  */
+#ifdef PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_OFF
+
+static_assert ((PROCESS_CREATION_MITIGATION_POLICY_FORCE_RELOCATE_IMAGES_ALWAYS_OFF
+               | PROCESS_CREATION_MITIGATION_POLICY_BOTTOM_UP_ASLR_ALWAYS_OFF)
+              == relocate_aslr_flags,
+              "check that ASLR flag values are correct");
+
+static_assert (PROC_THREAD_ATTRIBUTE_MITIGATION_POLICY == mitigation_policy,
+              "check that mitigation policy value is correct");
+
+#endif
+
+/* Helper template for the CreateProcess wrappers.
+
+   FUNC is the type of the underlying CreateProcess call.  CHAR is the
+   character type to use, and INFO is the "startupinfo" type to use.
+
+   DO_CREATE_PROCESS is the underlying CreateProcess function to use;
+   the remaining arguments are passed to it.  */
 template<typename FUNC, typename CHAR, typename INFO>
 BOOL
 create_process_wrapper (FUNC *do_create_process, const CHAR *image,
                        CHAR *command_line, DWORD flags,
                        void *environment, const CHAR *cur_dir,
+                       bool no_randomization,
                        INFO *startup_info,
                        PROCESS_INFORMATION *process_info)
 {
+  if (no_randomization && disable_randomization_available ())
+    {
+      static bool tried_and_failed;
+
+      if (!tried_and_failed)
+       {
+         /* Windows 8 is required for the real declaration, but to
+            allow building on earlier versions of Windows, we declare
+            the type locally.  */
+         struct gdb_extended_info
+         {
+           INFO StartupInfo;
+           gdb_lpproc_thread_attribute_list lpAttributeList;
+         };
+
+         gdb_extended_info info_ex {};
+
+         if (startup_info != nullptr)
+           info_ex.StartupInfo = *startup_info;
+         info_ex.StartupInfo.cb = sizeof (info_ex);
+         SIZE_T size = 0;
+         /* Ignore the result here.  The documentation says the first
+            call always fails, by design.  */
+         InitializeProcThreadAttributeList (nullptr, 1, 0, &size);
+         info_ex.lpAttributeList
+           = (PPROC_THREAD_ATTRIBUTE_LIST) alloca (size);
+         InitializeProcThreadAttributeList (info_ex.lpAttributeList,
+                                            1, 0, &size);
+
+         gdb::optional<BOOL> return_value;
+         DWORD attr_flags = relocate_aslr_flags;
+         if (!UpdateProcThreadAttribute (info_ex.lpAttributeList, 0,
+                                         mitigation_policy,
+                                         &attr_flags,
+                                         sizeof (attr_flags),
+                                         nullptr, nullptr))
+           tried_and_failed = true;
+         else
+           {
+             BOOL result = do_create_process (image, command_line,
+                                              nullptr, nullptr,
+                                              TRUE,
+                                              (flags
+                                               | EXTENDED_STARTUPINFO_PRESENT),
+                                              environment,
+                                              cur_dir,
+                                              (STARTUPINFO *) &info_ex,
+                                              process_info);
+             if (result)
+               return_value = result;
+             else if (GetLastError () == ERROR_INVALID_PARAMETER)
+               tried_and_failed = true;
+             else
+               return_value = FALSE;
+           }
+
+         DeleteProcThreadAttributeList (info_ex.lpAttributeList);
+
+         if (return_value.has_value ())
+           return *return_value;
+       }
+    }
+
   return do_create_process (image,
                            command_line, /* command line */
                            nullptr,      /* Security */
@@ -767,11 +860,12 @@ create_process_wrapper (FUNC *do_create_process, const CHAR *image,
 BOOL
 create_process (const char *image, char *command_line, DWORD flags,
                void *environment, const char *cur_dir,
+               bool no_randomization,
                STARTUPINFOA *startup_info,
                PROCESS_INFORMATION *process_info)
 {
   return create_process_wrapper (CreateProcessA, image, command_line, flags,
-                                environment, cur_dir,
+                                environment, cur_dir, no_randomization,
                                 startup_info, process_info);
 }
 
@@ -782,11 +876,12 @@ create_process (const char *image, char *command_line, DWORD flags,
 BOOL
 create_process (const wchar_t *image, wchar_t *command_line, DWORD flags,
                void *environment, const wchar_t *cur_dir,
+               bool no_randomization,
                STARTUPINFOW *startup_info,
                PROCESS_INFORMATION *process_info);
 {
   return create_process_wrapper (CreateProcessW, image, command_line, flags,
-                                environment, cur_dir,
+                                environment, cur_dir, no_randomization,
                                 startup_info, process_info);
 }
 
@@ -826,6 +921,16 @@ bad_GetConsoleFontSize (HANDLE w, DWORD nFont)
  
 /* See windows-nat.h.  */
 
+bool
+disable_randomization_available ()
+{
+  return (InitializeProcThreadAttributeList != nullptr
+         && UpdateProcThreadAttribute != nullptr
+         && DeleteProcThreadAttributeList != nullptr);
+}
+
+/* See windows-nat.h.  */
+
 bool
 initialize_loadable ()
 {
@@ -852,6 +957,10 @@ initialize_loadable ()
 #endif
       GPA (hm, GenerateConsoleCtrlEvent);
       GPA (hm, GetThreadDescription);
+
+      GPA (hm, InitializeProcThreadAttributeList);
+      GPA (hm, UpdateProcThreadAttribute);
+      GPA (hm, DeleteProcThreadAttributeList);
     }
 
   /* Set variables to dummy versions of these processes if the function
index d8c498ef06e5f2b94ed92a3ede6f37bcc3b3eebf..65b789fa59365552d4a95ee15a2abeac68c45a90 100644 (file)
@@ -294,17 +294,21 @@ extern BOOL continue_last_debug_event (DWORD continue_status,
 
 extern BOOL wait_for_debug_event (DEBUG_EVENT *event, DWORD timeout);
 
-/* Wrappers for CreateProcess.  */
+/* Wrappers for CreateProcess.  These exist primarily so that the
+   "disable randomization" feature can be implemented in a single
+   place.  */
 
 extern BOOL create_process (const char *image, char *command_line,
                            DWORD flags, void *environment,
                            const char *cur_dir,
+                           bool no_randomization,
                            STARTUPINFOA *startup_info,
                            PROCESS_INFORMATION *process_info);
 #ifdef __CYGWIN__
 extern BOOL create_process (const wchar_t *image, wchar_t *command_line,
                            DWORD flags, void *environment,
                            const wchar_t *cur_dir,
+                           bool no_randomization,
                            STARTUPINFOW *startup_info,
                            PROCESS_INFORMATION *process_info);
 #endif /* __CYGWIN__ */
@@ -313,10 +317,15 @@ extern BOOL create_process (const wchar_t *image, wchar_t *command_line,
 #define DebugActiveProcessStop         dyn_DebugActiveProcessStop
 #define DebugBreakProcess              dyn_DebugBreakProcess
 #define DebugSetProcessKillOnExit      dyn_DebugSetProcessKillOnExit
+#undef EnumProcessModules
 #define EnumProcessModules             dyn_EnumProcessModules
+#undef EnumProcessModulesEx
 #define EnumProcessModulesEx           dyn_EnumProcessModulesEx
+#undef GetModuleInformation
 #define GetModuleInformation           dyn_GetModuleInformation
+#undef GetModuleFileNameExA
 #define GetModuleFileNameExA           dyn_GetModuleFileNameExA
+#undef GetModuleFileNameExW
 #define GetModuleFileNameExW           dyn_GetModuleFileNameExW
 #define LookupPrivilegeValueA          dyn_LookupPrivilegeValueA
 #define OpenProcessToken               dyn_OpenProcessToken
@@ -327,6 +336,9 @@ extern BOOL create_process (const wchar_t *image, wchar_t *command_line,
 #define Wow64SetThreadContext          dyn_Wow64SetThreadContext
 #define Wow64GetThreadSelectorEntry    dyn_Wow64GetThreadSelectorEntry
 #define GenerateConsoleCtrlEvent       dyn_GenerateConsoleCtrlEvent
+#define InitializeProcThreadAttributeList dyn_InitializeProcThreadAttributeList
+#define UpdateProcThreadAttribute dyn_UpdateProcThreadAttribute
+#define DeleteProcThreadAttributeList dyn_DeleteProcThreadAttributeList
 
 typedef BOOL WINAPI (AdjustTokenPrivileges_ftype) (HANDLE, BOOL,
                                                   PTOKEN_PRIVILEGES,
@@ -397,6 +409,30 @@ extern Wow64GetThreadSelectorEntry_ftype *Wow64GetThreadSelectorEntry;
 typedef BOOL WINAPI (GenerateConsoleCtrlEvent_ftype) (DWORD, DWORD);
 extern GenerateConsoleCtrlEvent_ftype *GenerateConsoleCtrlEvent;
 
+/* We use a local typedef for this type to avoid depending on
+   Windows 8.  */
+typedef void *gdb_lpproc_thread_attribute_list;
+
+typedef BOOL WINAPI (InitializeProcThreadAttributeList_ftype)
+     (gdb_lpproc_thread_attribute_list lpAttributeList,
+      DWORD dwAttributeCount, DWORD dwFlags, PSIZE_T lpSize);
+extern InitializeProcThreadAttributeList_ftype *InitializeProcThreadAttributeList;
+
+typedef BOOL WINAPI (UpdateProcThreadAttribute_ftype)
+     (gdb_lpproc_thread_attribute_list lpAttributeList,
+      DWORD dwFlags, DWORD_PTR Attribute, PVOID lpValue, SIZE_T cbSize,
+      PVOID lpPreviousValue, PSIZE_T lpReturnSize);
+extern UpdateProcThreadAttribute_ftype *UpdateProcThreadAttribute;
+
+typedef void WINAPI (DeleteProcThreadAttributeList_ftype)
+     (gdb_lpproc_thread_attribute_list lpAttributeList);
+extern DeleteProcThreadAttributeList_ftype *DeleteProcThreadAttributeList;
+
+/* Return true if it's possible to disable randomization on this
+   host.  */
+
+extern bool disable_randomization_available ();
+
 /* Load any functions which may not be available in ancient versions
    of Windows.  */
 
index 262619d9cb7b97e367b40e11fbacbdb12ce12377..0f9fde9f8043b9efe4d3897fa38976013d3fdd2b 100644 (file)
@@ -281,6 +281,11 @@ struct windows_nat_target final : public x86_nat_target<inf_child_target>
   int get_windows_debug_event (int pid, struct target_waitstatus *ourstatus);
 
   void do_initial_windows_stuff (DWORD pid, bool attaching);
+
+  bool supports_disable_randomization () override
+  {
+    return disable_randomization_available ();
+  }
 };
 
 static windows_nat_target the_windows_nat_target;
@@ -2493,6 +2498,7 @@ windows_nat_target::create_inferior (const char *exec_file,
   windows_init_thread_list ();
   ret = create_process (args, flags, w32_env,
                        inferior_cwd != nullptr ? infcwd : nullptr,
+                       disable_randomization,
                        &si, &pi);
   if (w32_env)
     /* Just free the Win32 environment, if it could be created. */
@@ -2612,6 +2618,7 @@ windows_nat_target::create_inferior (const char *exec_file,
                        flags,  /* start flags */
                        w32env, /* environment */
                        inferior_cwd, /* current directory */
+                       disable_randomization,
                        &si,
                        &pi);
   if (tty != INVALID_HANDLE_VALUE)
index 00ce2a52feb8f920cafa9b1c3b03c5ba5711ec9a..a9af6467d7c460c272ad379c66a4479c87304acb 100644 (file)
@@ -586,6 +586,7 @@ create_process (const char *program, char *args,
                        (inferior_cwd.empty ()
                         ? NULL
                         : gdb_tilde_expand (inferior_cwd.c_str ()).c_str()),
+                       get_client_state ().disable_randomization,
                        &si,               /* start info */
                        pi);               /* proc info */
 
index d16f1f9609c32ace95eb28f2384c26553e401701..b29e8b3a10a887a567c5a81e785ad8be07ea5227 100644 (file)
@@ -165,6 +165,11 @@ public:
   { return true; }
 
   const char *pid_to_exec_file (int pid) override;
+
+  bool supports_disable_randomization () override
+  {
+    return windows_nat::disable_randomization_available ();
+  }
 };
 
 /* The sole Windows process.  */