Re: [PATCH v2] Improve ptrace-error detection on Linux targets

Christian Biesinger via gdb-patches
2019-08-26T18:35:55Z

On Mon, Aug 26, 2019 at 1:32 PM Sergio Durigan Junior
<sergiodj@redhat.com> wrote:
> @@ -30,11 +32,70 @@
> of 0 means there are no supported features. */
> static int supported_ptrace_options = -1;
>
> -/* Find all possible reasons we could fail to attach PID and return these
> - as a string. An empty string is returned if we didn't find any reason. */
> +typedef int (*selinux_ftype) (const char *);
> +
> +/* Helper function which checks if ptrace is probably restricted
> + (i.e., if ERR is either EACCES or EPERM), and returns a string with
> + possible workarounds. */
> +
> +static std::string
> +linux_ptrace_restricted_fail_reason (int err)
> +{
> + if (err != EACCES && err != EPERM)
> + {
> + /* It just makes sense to perform the checks below if errno was
> + either EACCES or EPERM. */
> + return {};
> + }
> +
> + std::string ret;
> + gdb_dlhandle_up handle = gdb_dlopen ("libselinux.so", true);

I would dlopen libselinux.so.1 instead so that this works even without
the -dev package.

> +
> + if (handle.get () != NULL)
> + {
> + selinux_ftype selinux_get_bool
> + = (selinux_ftype) gdb_dlsym (handle, "security_get_boolean_active");
> +
> + if (selinux_get_bool != NULL
> + && (*selinux_get_bool) ("deny_ptrace") == 1)
> + string_appendf (ret,
> + _("\n\
> +The SELinux 'deny_ptrace' option is enabled and preventing GDB\n\
> +from using 'ptrace'. Please, disable it by executing (as root):\n\
> +\n\
> + setsebool deny_ptrace off\n"));
> + }
> +
> + FILE *f = fopen ("/proc/sys/kernel/yama/ptrace_scope", "r");
> +
> + if (f != NULL)
> + {
> + char yama_scope = fgetc (f);
> +
> + fclose (f);
> +
> + if (yama_scope != '0')
> + string_appendf (ret,
> + _("\n\
> +The Linux kernel's Yama ptrace scope is in effect, which can prevent\n\
> +GDB from using 'ptrace'. Please, disable it by executing (as root):\n\
> +\n\
> + echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
> + }
> +
> + if (ret.empty ())
> + ret = _("\n\
> +There might be restrictions preventing ptrace from working. Please see\n\
> +the appendix \"Linux kernel ptrace restrictions\" in the GDB documentation\n\
> +for more details.\n");
> +
> + return ret;
> +}
> +
> +/* See declaration in linux-ptrace.h. */
>
> std::string
> -linux_ptrace_attach_fail_reason (pid_t pid)
> +linux_ptrace_attach_fail_reason (pid_t pid, int err)
> {
> pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
> std::string result;
> @@ -50,6 +111,11 @@ linux_ptrace_attach_fail_reason (pid_t pid)
> "terminated"),
> (int) pid);
>
> + std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
> +
> + if (!ptrace_restrict.empty ())
> + result += "\n" + ptrace_restrict;
> +
> return result;
> }
>
> @@ -68,6 +134,14 @@ linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
> return string_printf ("%s (%d)", safe_strerror (err), err);
> }
>
> +/* See linux-ptrace.h. */
> +
> +std::string
> +linux_ptrace_me_fail_reason (int err)
> +{
> + return linux_ptrace_restricted_fail_reason (err);
> +}
> +
> #if defined __i386__ || defined __x86_64__
>
> /* Address of the 'ret' instruction in asm code block below. */
> @@ -321,7 +395,11 @@ linux_grandchild_function (void *child_stack)
> static int
> linux_child_function (void *child_stack)
> {
> - ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0, (PTRACE_TYPE_ARG4) 0);
> + if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
> + (PTRACE_TYPE_ARG4) 0) != 0)
> + trace_start_error_with_name ("ptrace",
> + linux_ptrace_me_fail_reason (errno).c_str ());
> +
> kill (getpid (), SIGSTOP);
>
> /* Fork a grandchild. */
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index fd2f12a342..04ada53bf6 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -176,13 +176,26 @@ struct buffer;
> # define TRAP_HWBKPT 4
> #endif
>
> -extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
> +/* Find all possible reasons we could fail to attach PID and return
> + these as a string. An empty string is returned if we didn't find
> + any reason. If ERR is EACCES or EPERM, we also add a warning about
> + possible restrictions to use ptrace. */
> +extern std::string linux_ptrace_attach_fail_reason (pid_t pid, int err = -1);
>
> /* Find all possible reasons we could have failed to attach to PTID
> and return them as a string. ERR is the error PTRACE_ATTACH failed
> with (an errno). */
> extern std::string linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err);
>
> +/* When the call to 'ptrace (PTRACE_TRACEME...' fails, and we have
> + already forked, this function can be called in order to try to
> + obtain the reason why ptrace failed. ERR should be the ERRNO value
> + returned by ptrace.
> +
> + This function will return a 'std::string' containing the fail
> + reason, or an empty string otherwise. */
> +extern std::string linux_ptrace_me_fail_reason (int err);
> +
> extern void linux_ptrace_init_warnings (void);
> extern void linux_check_ptrace_features (void);
> extern void linux_enable_event_reporting (pid_t pid, int attached);
> --
> 2.21.0
>