From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mail-il1-x133.google.com (mail-il1-x133.google.com [IPv6:2607:f8b0:4864:20::133]) by sourceware.org (Postfix) with ESMTPS id 885E03858C3A for ; Fri, 11 Mar 2022 18:57:08 +0000 (GMT) DMARC-Filter: OpenDMARC Filter v1.4.1 sourceware.org 885E03858C3A Received: by mail-il1-x133.google.com with SMTP id n16so5432900ile.11 for ; Fri, 11 Mar 2022 10:57:08 -0800 (PST) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Ii37jypC43qfvb3zdQ8jworKG8kMIFojbkhVIcxRBAQ=; b=xnw00vBHJLpKwOIUxNv2FaX7UfaA9H0SVqPjUNUkw6/wzQanq3HkHe+6n9na0mR9+r tKZ4TBPrMrTAGH76LBHjmxC60vQ/M+t86UbVvC1tSCnVmq3hXrH/9yZ47uNxK1fgk1QA G1kyyfSNbYiIG2n57kvma5KgYTv3SQ9uYkmy0BvI0PTApKYIF7aJV/9GTMImeo+zt58+ VRPwH/uUCLGL1c6xLOmG4qY/wi2Ci5MUygDwMQkxb3LhNqADsFDecjZ9l49VtYf8LLJg 24gUQVqvDy/4IbHuqOQ+jZ6/D/T2RM9I7phEddKN1to99IbLmPRoe+c/rUHi4C9YSZEd CKTw== X-Gm-Message-State: AOAM531BgObe3MvkcPtrAgWBL04i0SOrgiTvw4sr/l6iDVTv4fjPmwVg 5p2h+Lax8pns1YsZun0r60RugZNf7OdYCA== X-Google-Smtp-Source: ABdhPJzGS0NAXjG7RP6BwapbIIaIfSOdKfqK/Bu09JVgvu9rsEEr4WsKBxmXZS+YwtWYYacz2dVvtQ== X-Received: by 2002:a05:6e02:1a6e:b0:2c1:e50c:666a with SMTP id w14-20020a056e021a6e00b002c1e50c666amr8927473ilv.40.1647025027737; Fri, 11 Mar 2022 10:57:07 -0800 (PST) Received: from murgatroyd.Home (75-166-141-253.hlrn.qwest.net. [75.166.141.253]) by smtp.gmail.com with ESMTPSA id o6-20020a056e02188600b002c61b4fef99sm5129268ilu.1.2022.03.11.10.57.07 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 11 Mar 2022 10:57:07 -0800 (PST) From: Tom Tromey To: gdb-patches@sourceware.org Cc: Tom Tromey Subject: [PATCH v2 2/2] Allow ASLR to be disabled on Windows Date: Fri, 11 Mar 2022 11:57:05 -0700 Message-Id: <20220311185705.774197-3-tromey@adacore.com> X-Mailer: git-send-email 2.34.1 In-Reply-To: <20220311185705.774197-1-tromey@adacore.com> References: <20220311185705.774197-1-tromey@adacore.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Spam-Status: No, score=-11.3 required=5.0 tests=BAYES_00, DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, DKIM_VALID_EF, GIT_PATCH_0, RCVD_IN_DNSWL_NONE, SPF_HELO_NONE, SPF_PASS, TXREP, T_SCC_BODY_TEXT_LINE autolearn=ham autolearn_force=no version=3.4.4 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on server2.sourceware.org X-BeenThere: gdb-patches@sourceware.org X-Mailman-Version: 2.1.29 Precedence: list List-Id: Gdb-patches mailing list List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Fri, 11 Mar 2022 18:57:11 -0000 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 | 2 + gdb/nat/windows-nat.c | 115 +++++++++++++++++++++++++++++++++++++++-- gdb/nat/windows-nat.h | 38 +++++++++++++- gdb/windows-nat.c | 7 +++ gdbserver/win32-low.cc | 1 + gdbserver/win32-low.h | 5 ++ 6 files changed, 164 insertions(+), 4 deletions(-) diff --git a/gdb/NEWS b/gdb/NEWS index c9b6f42616b..b22f8825b99 100644 --- a/gdb/NEWS +++ b/gdb/NEWS @@ -9,6 +9,8 @@ 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 diff --git a/gdb/nat/windows-nat.c b/gdb/nat/windows-nat.c index fdfc8e702f8..cb9f10afa06 100644 --- a/gdb/nat/windows-nat.c +++ b/gdb/nat/windows-nat.c @@ -68,6 +68,10 @@ Wow64GetThreadSelectorEntry_ftype *Wow64GetThreadSelectorEntry; #endif GenerateConsoleCtrlEvent_ftype *GenerateConsoleCtrlEvent; +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, ...) \ @@ -579,15 +583,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 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 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 */ @@ -605,11 +698,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); } @@ -620,11 +714,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); } @@ -664,6 +759,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 () { @@ -689,6 +794,10 @@ initialize_loadable () GPA (hm, Wow64GetThreadSelectorEntry); #endif GPA (hm, GenerateConsoleCtrlEvent); + + GPA (hm, InitializeProcThreadAttributeList); + GPA (hm, UpdateProcThreadAttribute); + GPA (hm, DeleteProcThreadAttributeList); } /* Set variables to dummy versions of these processes if the function diff --git a/gdb/nat/windows-nat.h b/gdb/nat/windows-nat.h index a0267cd96ba..722de895498 100644 --- a/gdb/nat/windows-nat.h +++ b/gdb/nat/windows-nat.h @@ -263,17 +263,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__ */ @@ -282,10 +286,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 @@ -296,6 +305,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, @@ -366,6 +378,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. */ diff --git a/gdb/windows-nat.c b/gdb/windows-nat.c index 251876c7022..b2e27d9cb52 100644 --- a/gdb/windows-nat.c +++ b/gdb/windows-nat.c @@ -281,6 +281,11 @@ struct windows_nat_target final : public x86_nat_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; @@ -2615,6 +2620,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. */ @@ -2734,6 +2740,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) diff --git a/gdbserver/win32-low.cc b/gdbserver/win32-low.cc index 5164da59a21..67a10fceca6 100644 --- a/gdbserver/win32-low.cc +++ b/gdbserver/win32-low.cc @@ -580,6 +580,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 */ diff --git a/gdbserver/win32-low.h b/gdbserver/win32-low.h index 8856a84baa3..357854c4e82 100644 --- a/gdbserver/win32-low.h +++ b/gdbserver/win32-low.h @@ -158,6 +158,11 @@ class win32_process_target : public process_stratum_target bool stopped_by_sw_breakpoint () override; bool supports_stopped_by_sw_breakpoint () override; + + bool supports_disable_randomization () override + { + return windows_nat::disable_randomization_available (); + } }; /* Retrieve the context for this thread, if not already retrieved. */ -- 2.34.1