public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
* [PATCH] Improve ptrace-error detection on Linux targets
@ 2019-08-19  3:29 Sergio Durigan Junior
  2019-08-19  9:10 ` Ruslan Kabatsayev
                   ` (6 more replies)
  0 siblings, 7 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-19  3:29 UTC (permalink / raw)
  To: GDB Patches; +Cc: Sergio Durigan Junior

In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

Instead of printing a very certain warning about a very specific
setting, I decided to propose the following patch, which prints a
generic warning about a possible situation (i.e., without going
bothering to make sure that the situation is actually happening).
What this means is that whenever a ptrace error is detected, and if
errno is either EACCES or EPERM, a warning will be printed pointing
the user to our documentation, where she will find more details about
possible restrictions in place against ptrace.

The patch obviously expands our documentation and creates a new
appendix section named "Linux kernel ptrace restrictions", with
sub-sections for each possible restriction that might be in place.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option.

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting.

  - seccomp on Docker containers.

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

For the PTRACE_ATTACH scenario, I borrowed the original patch's idea
and hacked the warning into 'linux_ptrace_attach_fail_reason'.  For
PTRACE_ME, I opted to implement a new target method called
'ptrace_me_fail_reason' which is then be called inside inf-ptrace.c's
'inf_ptrace_me'.  This method is currently only implemented by the
Linux target.

I tested this patch locally, on my Fedora 30 machine (actually, a
Fedora Rawhide VM), but I'm not proposing a testcase for it because of
the difficulty of writing one.  Maybe something like using
dlopen (RTLD_NEXT... and wrapping ptrace could work, but I don't know
offhand.

WDYT?

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* inf-ptrace.c (inf_ptrace_me): Update call to
	'trace_start_error_with_name'.
	* linux-nat.c (linux_nat_target::ptrace_me_fail_reason): New
	method.
	(linux_nat_target::attach): Update call to
	'linux_ptrace_attach_fail_reason'.
	* linux-nat.h (linux_nat_target) <ptrace_me_fail_reason>: New
	method.
	* nat/fork-inferior.c (trace_start_error_with_name): Add
	optional 'append' argument.
	* nat/fork-inferior.h (trace_start_error_with_name): Update
	prototype.
	* nat/linux-ptrace.c (linux_ptrace_attach_fail_reason): Add
	optional 'err' argument.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
	prototype.
	* target-delegates.c: Regenerate.
	* target.h (struct target_ops) <ptrace_me_fail_reason>: New
	method.
	(target_ptrace_me_fail_reason): New define.
---
 gdb/doc/gdb.texinfo     | 90 +++++++++++++++++++++++++++++++++++++++++
 gdb/inf-ptrace.c        |  3 +-
 gdb/linux-nat.c         | 19 ++++++++-
 gdb/linux-nat.h         |  2 +
 gdb/nat/fork-inferior.c |  4 +-
 gdb/nat/fork-inferior.h |  7 ++--
 gdb/nat/linux-ptrace.c  | 11 +++--
 gdb/nat/linux-ptrace.h  |  6 ++-
 gdb/target-delegates.c  | 28 +++++++++++++
 gdb/target.h            | 15 +++++++
 10 files changed, 174 insertions(+), 11 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index bcf0420779..d2aab9be45 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel @code{ptrace} restrictions:: Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -44672,6 +44675,93 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel @code{ptrace} restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} to, among other
+things, attach to a new or existing inferior in order to start
+debugging it.  Due to security concerns, some distributions and
+vendors disable or severily restrict the ability to perform these
+operations, which can make @value{GDBN} malfunction.  In this section,
+we will expand on how this malfunction can manifest, and how to modify
+the system's settings in order to be able to use @value{GDBN}
+properly.
+
+@menu
+* SELinux's @code{deny_ptrace}::        SELinux and the @code{deny_ptrace} option
+* Yama's @code{ptrace_scope}::          Yama and the @code{ptrace_scope} setting
+* Docker and @code{seccomp}::           Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node SELinux's @code{deny_ptrace}
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex selinux, deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do:
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's @code{ptrace_scope}
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} can be affected
+by it.  You can temporarily disable the feature by doing:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and @code{seccomp}
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN}.  To do that, you can use the
+options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
+when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 4a8e732373..4e21f677a2 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -101,7 +101,8 @@ inf_ptrace_me (void)
 {
   /* "Trace me, Dr. Memory!"  */
   if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 target_ptrace_me_fail_reason (errno).c_str ());
 }
 
 /* Start a new inferior Unix child process.  EXEC_FILE is the file to
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 945c19f666..053d7630b8 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -614,6 +614,22 @@ linux_nat_target::follow_fork (int follow_child, int detach_fork)
   return 0;
 }
 
+/* See the declaration on target.h.  */
+
+std::string
+linux_nat_target::ptrace_me_fail_reason (int err)
+{
+  std::string ret;
+
+  if (err == EACCES || err == EPERM)
+    ret = _("There might be restrictions preventing ptrace from\n"
+	    "working.  Please see the appendix "
+	    "\"Linux kernel ptrace restrictions\"\n"
+	    "in the GDB documentation for more details.");
+
+  return ret;
+}
+
 \f
 int
 linux_nat_target::insert_fork_catchpoint (int pid)
@@ -1191,8 +1207,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
index 0c1695ad10..1aa9b50130 100644
--- a/gdb/linux-nat.h
+++ b/gdb/linux-nat.h
@@ -134,6 +134,8 @@ public:
 
   int follow_fork (int, int) override;
 
+  std::string ptrace_me_fail_reason (int err) override;
+
   std::vector<static_tracepoint_marker>
     static_tracepoint_markers_by_strid (const char *id) override;
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 68b51aa814..72ac623e20 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index 1d0519fb26..7e6b889210 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
 #endif /* NAT_FORK_INFERIOR_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index c1ebc0a032..fa5584829e 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -30,11 +30,10 @@
    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.  */
+/* 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 +49,12 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 		      "terminated"),
 		    (int) pid);
 
+  if (err == EACCES || err == EPERM)
+    string_appendf (result,
+		    _("There might be restrictions preventing ptrace from\n"
+		      "working.  Please see the appendix "
+		      "\"Linux kernel ptrace restrictions\"\n"
+		      "in the GDB documentation for more details."));
   return result;
 }
 
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index fd2f12a342..99a7780ac7 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,7 +176,11 @@ 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
diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
index 52034fe436..7fe45412cb 100644
--- a/gdb/target-delegates.c
+++ b/gdb/target-delegates.c
@@ -52,6 +52,7 @@ struct dummy_target : public target_ops
   void kill () override;
   void load (const char *arg0, int arg1) override;
   void post_startup_inferior (ptid_t arg0) override;
+  std::string ptrace_me_fail_reason (int arg0) override;
   int insert_fork_catchpoint (int arg0) override;
   int remove_fork_catchpoint (int arg0) override;
   int insert_vfork_catchpoint (int arg0) override;
@@ -220,6 +221,7 @@ struct debug_target : public target_ops
   void kill () override;
   void load (const char *arg0, int arg1) override;
   void post_startup_inferior (ptid_t arg0) override;
+  std::string ptrace_me_fail_reason (int arg0) override;
   int insert_fork_catchpoint (int arg0) override;
   int remove_fork_catchpoint (int arg0) override;
   int insert_vfork_catchpoint (int arg0) override;
@@ -1400,6 +1402,32 @@ debug_target::post_startup_inferior (ptid_t arg0)
   fputs_unfiltered (")\n", gdb_stdlog);
 }
 
+std::string
+target_ops::ptrace_me_fail_reason (int arg0)
+{
+  return this->beneath ()->ptrace_me_fail_reason (arg0);
+}
+
+std::string
+dummy_target::ptrace_me_fail_reason (int arg0)
+{
+  return std::string ();
+}
+
+std::string
+debug_target::ptrace_me_fail_reason (int arg0)
+{
+  std::string result;
+  fprintf_unfiltered (gdb_stdlog, "-> %s->ptrace_me_fail_reason (...)\n", this->beneath ()->shortname ());
+  result = this->beneath ()->ptrace_me_fail_reason (arg0);
+  fprintf_unfiltered (gdb_stdlog, "<- %s->ptrace_me_fail_reason (", this->beneath ()->shortname ());
+  target_debug_print_int (arg0);
+  fputs_unfiltered (") = ", gdb_stdlog);
+  target_debug_print_std_string (result);
+  fputs_unfiltered ("\n", gdb_stdlog);
+  return result;
+}
+
 int
 target_ops::insert_fork_catchpoint (int arg0)
 {
diff --git a/gdb/target.h b/gdb/target.h
index 4e2e75cb80..8e6cc75d8e 100644
--- a/gdb/target.h
+++ b/gdb/target.h
@@ -608,6 +608,18 @@ struct target_ops
 				  char **, int);
     virtual void post_startup_inferior (ptid_t)
       TARGET_DEFAULT_IGNORE ();
+    /* When the call to ptrace fails, and we have already forked, this
+       function can be called in order to try to obtain the reason why
+       ptrace failed.  It takes one integer argument, which should be
+       the ERRNO value returned by ptrace.
+
+       This function will return a 'std::string' containing the fail
+       reason, or an empty string otherwise.
+
+       The reason this is a target method is because different targets
+       can compute the reason in different ways.  */
+    virtual std::string ptrace_me_fail_reason (int)
+      TARGET_DEFAULT_RETURN (std::string ());
     virtual int insert_fork_catchpoint (int)
       TARGET_DEFAULT_RETURN (1);
     virtual int remove_fork_catchpoint (int)
@@ -1624,6 +1636,9 @@ extern void target_load (const char *arg, int from_tty);
 #define target_remove_vfork_catchpoint(pid) \
      (current_top_target ()->remove_vfork_catchpoint) (pid)
 
+#define target_ptrace_me_fail_reason(err) \
+     (current_top_target ()->ptrace_me_fail_reason) (err)
+
 /* If the inferior forks or vforks, this function will be called at
    the next resume in order to perform any bookkeeping and fiddling
    necessary to continue debugging either the parent or child, as
-- 
2.21.0

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
@ 2019-08-19  9:10 ` Ruslan Kabatsayev
  2019-08-19 13:47   ` Sergio Durigan Junior
  2019-08-19 14:33 ` Eli Zaretskii
                   ` (5 subsequent siblings)
  6 siblings, 1 reply; 98+ messages in thread
From: Ruslan Kabatsayev @ 2019-08-19  9:10 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches

Hello Sergio,

On Mon, 19 Aug 2019 at 06:29, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
>
> In Fedora GDB, we carry the following patch:
>
>   https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch
>
> Its purpose is to try to detect a specific scenario where SELinux's
> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
> order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
> with EACCES in this case).
>
> I like the idea of improving error detection and providing more
> information to the user (a simple "Permission denied" can be really
> frustrating), but I don't fully agree with the way the patch was
> implemented: it makes GDB link against libselinux only for the sake of
> consulting the 'deny_ptrace' setting, and then prints a warning if
> ptrace failed and this setting is on.
>
> Instead of printing a very certain warning about a very specific
> setting, I decided to propose the following patch, which prints a
> generic warning about a possible situation (i.e., without going
> bothering to make sure that the situation is actually happening).
> What this means is that whenever a ptrace error is detected, and if
> errno is either EACCES or EPERM, a warning will be printed pointing
> the user to our documentation, where she will find more details about
> possible restrictions in place against ptrace.

Doesn't it worsen user experience compared to the current Fedora
patch? Now a user first coming across the problem will have to
navigate the documentation, then check possible scenarios – instead of
simply getting a friendly explanation what exactly is wrong.

Compare this with the patch applied by some Linux distributions
(CentOS IIRC) to make Bash emit

> bash: myprogram: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory

when ELF interpreter can't be found. By default, Bash only prints the
less than helpful message

> bash: myprogram: No such file or directory

Your proposal (if it's intended to replace Fedora's GDB patch) is
similar to replacing this default with

> bash: myprogram: No such file or directory. There might be other reasons than simply command executable not existing. Please see Bash documentation section XYZ for details.

and adding a section XYZ in Bash docs explaining that the reasons
could include lacking libraries, broken symlink to the binary, stale
hash table of PATH lookups, etc.. This is considerably worse than the
patch with explicit announcement of actual problem.

So as a user, I disfavor this approach, although it might be more
maintainable for GDB developers. But, if dependency on libselinux is
the only objection, then why not simply dlopen it as needed (and
gracefully handle failure to do so) instead of ELF-level linking to
it? Then there'll be no hard dependency on libselinux, while user
experience will not degrade too.

Regards,
Ruslan

>
> The patch obviously expands our documentation and creates a new
> appendix section named "Linux kernel ptrace restrictions", with
> sub-sections for each possible restriction that might be in place.
>
> The current list of possible restrictions is:
>
>   - SELinux's 'deny_ptrace' option.
>
>   - YAMA's /proc/sys/kernel/yama/ptrace_scope setting.
>
>   - seccomp on Docker containers.
>
> It's important to mention that all of this is Linux-specific; as far
> as I know, SELinux, YAMA and seccomp are Linux-only features.
>
> For the PTRACE_ATTACH scenario, I borrowed the original patch's idea
> and hacked the warning into 'linux_ptrace_attach_fail_reason'.  For
> PTRACE_ME, I opted to implement a new target method called
> 'ptrace_me_fail_reason' which is then be called inside inf-ptrace.c's
> 'inf_ptrace_me'.  This method is currently only implemented by the
> Linux target.
>
> I tested this patch locally, on my Fedora 30 machine (actually, a
> Fedora Rawhide VM), but I'm not proposing a testcase for it because of
> the difficulty of writing one.  Maybe something like using
> dlopen (RTLD_NEXT... and wrapping ptrace could work, but I don't know
> offhand.
>
> WDYT?
>
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
>         * gdb.texinfo (Linux kernel ptrace restrictions): New appendix
>         section.
>
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>             Jan Kratochvil  <jan.kratochvil@redhat.com>
>
>         * inf-ptrace.c (inf_ptrace_me): Update call to
>         'trace_start_error_with_name'.
>         * linux-nat.c (linux_nat_target::ptrace_me_fail_reason): New
>         method.
>         (linux_nat_target::attach): Update call to
>         'linux_ptrace_attach_fail_reason'.
>         * linux-nat.h (linux_nat_target) <ptrace_me_fail_reason>: New
>         method.
>         * nat/fork-inferior.c (trace_start_error_with_name): Add
>         optional 'append' argument.
>         * nat/fork-inferior.h (trace_start_error_with_name): Update
>         prototype.
>         * nat/linux-ptrace.c (linux_ptrace_attach_fail_reason): Add
>         optional 'err' argument.
>         * nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
>         prototype.
>         * target-delegates.c: Regenerate.
>         * target.h (struct target_ops) <ptrace_me_fail_reason>: New
>         method.
>         (target_ptrace_me_fail_reason): New define.
> ---
>  gdb/doc/gdb.texinfo     | 90 +++++++++++++++++++++++++++++++++++++++++
>  gdb/inf-ptrace.c        |  3 +-
>  gdb/linux-nat.c         | 19 ++++++++-
>  gdb/linux-nat.h         |  2 +
>  gdb/nat/fork-inferior.c |  4 +-
>  gdb/nat/fork-inferior.h |  7 ++--
>  gdb/nat/linux-ptrace.c  | 11 +++--
>  gdb/nat/linux-ptrace.h  |  6 ++-
>  gdb/target-delegates.c  | 28 +++++++++++++
>  gdb/target.h            | 15 +++++++
>  10 files changed, 174 insertions(+), 11 deletions(-)
>
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index bcf0420779..d2aab9be45 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -182,6 +182,9 @@ software in general.  We will miss him.
>                                  @value{GDBN}
>  * Operating System Information:: Getting additional information from
>                                   the operating system
> +* Linux kernel @code{ptrace} restrictions:: Restrictions sometimes
> +                                            imposed by the Linux
> +                                            kernel on @code{ptrace}
>  * Trace File Format::          GDB trace file format
>  * Index Section Format::        .gdb_index section format
>  * Man Pages::                  Manual pages
> @@ -44672,6 +44675,93 @@ should contain a comma-separated list of cores that this process
>  is running on.  Target may provide additional columns,
>  which @value{GDBN} currently ignores.
>
> +@node Linux kernel @code{ptrace} restrictions
> +@appendix Linux kernel @code{ptrace} restrictions
> +@cindex linux kernel ptrace restrictions, attach
> +
> +The @code{ptrace} system call is used by @value{GDBN} to, among other
> +things, attach to a new or existing inferior in order to start
> +debugging it.  Due to security concerns, some distributions and
> +vendors disable or severily restrict the ability to perform these
> +operations, which can make @value{GDBN} malfunction.  In this section,
> +we will expand on how this malfunction can manifest, and how to modify
> +the system's settings in order to be able to use @value{GDBN}
> +properly.
> +
> +@menu
> +* SELinux's @code{deny_ptrace}::        SELinux and the @code{deny_ptrace} option
> +* Yama's @code{ptrace_scope}::          Yama and the @code{ptrace_scope} setting
> +* Docker and @code{seccomp}::           Docker and the @code{seccomp}
> +                                        infrastructure
> +@end menu
> +
> +@node SELinux's @code{deny_ptrace}
> +@appendixsection SELinux's @code{deny_ptrace}
> +@cindex selinux, deny_ptrace
> +
> +If you are using SELinux, you might want to check whether the
> +@code{deny_ptrace} option is enabled by doing:
> +
> +@smallexample
> +$ getsebool deny_ptrace
> +deny_ptrace --> on
> +@end smallexample
> +
> +If the option is enabled, you can disable it by doing, as root:
> +
> +@smallexample
> +# setsebool deny_ptrace off
> +@end smallexample
> +
> +The option will be disabled until the next reboot.  If you would like
> +to disable it permanently, you can do:
> +
> +@smallexample
> +# setsebool -P deny_ptrace off
> +@end smallexample
> +
> +@node Yama's @code{ptrace_scope}
> +@appendixsection Yama's @code{ptrace_scope}
> +@cindex yama, ptrace_scope
> +
> +If your system has Yama enabled, you might want to check whether the
> +@code{ptrace_scope} setting is enabled by checking the value of
> +@file{/proc/sys/kernel/yama/ptrace_scope}:
> +
> +@smallexample
> +$ cat /proc/sys/kernel/yama/ptrace_scope
> +0
> +@end smallexample
> +
> +If you see anything other than @code{0}, @value{GDBN} can be affected
> +by it.  You can temporarily disable the feature by doing:
> +
> +@smallexample
> +# sysctl kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample
> +
> +You can make this permanent by doing:
> +
> +@smallexample
> +# sysctl -w kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample
> +
> +@node Docker and @code{seccomp}
> +@appendixsection Docker and @code{seccomp}
> +@cindex docker, seccomp
> +
> +If you are using Docker (@uref{https://www.docker.com/}) containers,
> +you will probably have to disable its @code{seccomp} protections in
> +order to be able to use @value{GDBN}.  To do that, you can use the
> +options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
> +when invoking Docker:
> +
> +@smallexample
> +$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
> +@end smallexample
> +
>  @node Trace File Format
>  @appendix Trace File Format
>  @cindex trace file format
> diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
> index 4a8e732373..4e21f677a2 100644
> --- a/gdb/inf-ptrace.c
> +++ b/gdb/inf-ptrace.c
> @@ -101,7 +101,8 @@ inf_ptrace_me (void)
>  {
>    /* "Trace me, Dr. Memory!"  */
>    if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
> -    trace_start_error_with_name ("ptrace");
> +    trace_start_error_with_name ("ptrace",
> +                                target_ptrace_me_fail_reason (errno).c_str ());
>  }
>
>  /* Start a new inferior Unix child process.  EXEC_FILE is the file to
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index 945c19f666..053d7630b8 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -614,6 +614,22 @@ linux_nat_target::follow_fork (int follow_child, int detach_fork)
>    return 0;
>  }
>
> +/* See the declaration on target.h.  */
> +
> +std::string
> +linux_nat_target::ptrace_me_fail_reason (int err)
> +{
> +  std::string ret;
> +
> +  if (err == EACCES || err == EPERM)
> +    ret = _("There might be restrictions preventing ptrace from\n"
> +           "working.  Please see the appendix "
> +           "\"Linux kernel ptrace restrictions\"\n"
> +           "in the GDB documentation for more details.");
> +
> +  return ret;
> +}
> +
>
>  int
>  linux_nat_target::insert_fork_catchpoint (int pid)
> @@ -1191,8 +1207,9 @@ linux_nat_target::attach (const char *args, int from_tty)
>      }
>    catch (const gdb_exception_error &ex)
>      {
> +      int saved_errno = errno;
>        pid_t pid = parse_pid_to_attach (args);
> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>
>        if (!reason.empty ())
>         throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
> diff --git a/gdb/linux-nat.h b/gdb/linux-nat.h
> index 0c1695ad10..1aa9b50130 100644
> --- a/gdb/linux-nat.h
> +++ b/gdb/linux-nat.h
> @@ -134,6 +134,8 @@ public:
>
>    int follow_fork (int, int) override;
>
> +  std::string ptrace_me_fail_reason (int err) override;
> +
>    std::vector<static_tracepoint_marker>
>      static_tracepoint_markers_by_strid (const char *id) override;
>
> diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
> index 68b51aa814..72ac623e20 100644
> --- a/gdb/nat/fork-inferior.c
> +++ b/gdb/nat/fork-inferior.c
> @@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
>  /* See nat/fork-inferior.h.  */
>
>  void
> -trace_start_error_with_name (const char *string)
> +trace_start_error_with_name (const char *string, const char *append)
>  {
> -  trace_start_error ("%s: %s", string, safe_strerror (errno));
> +  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
>  }
> diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
> index 1d0519fb26..7e6b889210 100644
> --- a/gdb/nat/fork-inferior.h
> +++ b/gdb/nat/fork-inferior.h
> @@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
>    ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
>
>  /* Like "trace_start_error", but the error message is constructed by
> -   combining STRING with the system error message for errno.  This
> -   function does not return.  */
> -extern void trace_start_error_with_name (const char *string)
> +   combining STRING with the system error message for errno, and
> +   (optionally) with APPEND.  This function does not return.  */
> +extern void trace_start_error_with_name (const char *string,
> +                                        const char *append = "")
>    ATTRIBUTE_NORETURN;
>
>  #endif /* NAT_FORK_INFERIOR_H */
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index c1ebc0a032..fa5584829e 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -30,11 +30,10 @@
>     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.  */
> +/* 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 +49,12 @@ linux_ptrace_attach_fail_reason (pid_t pid)
>                       "terminated"),
>                     (int) pid);
>
> +  if (err == EACCES || err == EPERM)
> +    string_appendf (result,
> +                   _("There might be restrictions preventing ptrace from\n"
> +                     "working.  Please see the appendix "
> +                     "\"Linux kernel ptrace restrictions\"\n"
> +                     "in the GDB documentation for more details."));
>    return result;
>  }
>
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index fd2f12a342..99a7780ac7 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -176,7 +176,11 @@ 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
> diff --git a/gdb/target-delegates.c b/gdb/target-delegates.c
> index 52034fe436..7fe45412cb 100644
> --- a/gdb/target-delegates.c
> +++ b/gdb/target-delegates.c
> @@ -52,6 +52,7 @@ struct dummy_target : public target_ops
>    void kill () override;
>    void load (const char *arg0, int arg1) override;
>    void post_startup_inferior (ptid_t arg0) override;
> +  std::string ptrace_me_fail_reason (int arg0) override;
>    int insert_fork_catchpoint (int arg0) override;
>    int remove_fork_catchpoint (int arg0) override;
>    int insert_vfork_catchpoint (int arg0) override;
> @@ -220,6 +221,7 @@ struct debug_target : public target_ops
>    void kill () override;
>    void load (const char *arg0, int arg1) override;
>    void post_startup_inferior (ptid_t arg0) override;
> +  std::string ptrace_me_fail_reason (int arg0) override;
>    int insert_fork_catchpoint (int arg0) override;
>    int remove_fork_catchpoint (int arg0) override;
>    int insert_vfork_catchpoint (int arg0) override;
> @@ -1400,6 +1402,32 @@ debug_target::post_startup_inferior (ptid_t arg0)
>    fputs_unfiltered (")\n", gdb_stdlog);
>  }
>
> +std::string
> +target_ops::ptrace_me_fail_reason (int arg0)
> +{
> +  return this->beneath ()->ptrace_me_fail_reason (arg0);
> +}
> +
> +std::string
> +dummy_target::ptrace_me_fail_reason (int arg0)
> +{
> +  return std::string ();
> +}
> +
> +std::string
> +debug_target::ptrace_me_fail_reason (int arg0)
> +{
> +  std::string result;
> +  fprintf_unfiltered (gdb_stdlog, "-> %s->ptrace_me_fail_reason (...)\n", this->beneath ()->shortname ());
> +  result = this->beneath ()->ptrace_me_fail_reason (arg0);
> +  fprintf_unfiltered (gdb_stdlog, "<- %s->ptrace_me_fail_reason (", this->beneath ()->shortname ());
> +  target_debug_print_int (arg0);
> +  fputs_unfiltered (") = ", gdb_stdlog);
> +  target_debug_print_std_string (result);
> +  fputs_unfiltered ("\n", gdb_stdlog);
> +  return result;
> +}
> +
>  int
>  target_ops::insert_fork_catchpoint (int arg0)
>  {
> diff --git a/gdb/target.h b/gdb/target.h
> index 4e2e75cb80..8e6cc75d8e 100644
> --- a/gdb/target.h
> +++ b/gdb/target.h
> @@ -608,6 +608,18 @@ struct target_ops
>                                   char **, int);
>      virtual void post_startup_inferior (ptid_t)
>        TARGET_DEFAULT_IGNORE ();
> +    /* When the call to ptrace fails, and we have already forked, this
> +       function can be called in order to try to obtain the reason why
> +       ptrace failed.  It takes one integer argument, which should be
> +       the ERRNO value returned by ptrace.
> +
> +       This function will return a 'std::string' containing the fail
> +       reason, or an empty string otherwise.
> +
> +       The reason this is a target method is because different targets
> +       can compute the reason in different ways.  */
> +    virtual std::string ptrace_me_fail_reason (int)
> +      TARGET_DEFAULT_RETURN (std::string ());
>      virtual int insert_fork_catchpoint (int)
>        TARGET_DEFAULT_RETURN (1);
>      virtual int remove_fork_catchpoint (int)
> @@ -1624,6 +1636,9 @@ extern void target_load (const char *arg, int from_tty);
>  #define target_remove_vfork_catchpoint(pid) \
>       (current_top_target ()->remove_vfork_catchpoint) (pid)
>
> +#define target_ptrace_me_fail_reason(err) \
> +     (current_top_target ()->ptrace_me_fail_reason) (err)
> +
>  /* If the inferior forks or vforks, this function will be called at
>     the next resume in order to perform any bookkeeping and fiddling
>     necessary to continue debugging either the parent or child, as
> --
> 2.21.0
>

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19  9:10 ` Ruslan Kabatsayev
@ 2019-08-19 13:47   ` Sergio Durigan Junior
  2019-08-19 14:57     ` Ruslan Kabatsayev
  2019-08-19 16:30     ` Christian Biesinger via gdb-patches
  0 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-19 13:47 UTC (permalink / raw)
  To: Ruslan Kabatsayev; +Cc: GDB Patches

On Monday, August 19 2019, Ruslan Kabatsayev wrote:

> Hello Sergio,

Hey Ruslan,

> On Mon, 19 Aug 2019 at 06:29, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
>>
>> In Fedora GDB, we carry the following patch:
>>
>>   https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch
>>
>> Its purpose is to try to detect a specific scenario where SELinux's
>> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
>> order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
>> with EACCES in this case).
>>
>> I like the idea of improving error detection and providing more
>> information to the user (a simple "Permission denied" can be really
>> frustrating), but I don't fully agree with the way the patch was
>> implemented: it makes GDB link against libselinux only for the sake of
>> consulting the 'deny_ptrace' setting, and then prints a warning if
>> ptrace failed and this setting is on.
>>
>> Instead of printing a very certain warning about a very specific
>> setting, I decided to propose the following patch, which prints a
>> generic warning about a possible situation (i.e., without going
>> bothering to make sure that the situation is actually happening).
>> What this means is that whenever a ptrace error is detected, and if
>> errno is either EACCES or EPERM, a warning will be printed pointing
>> the user to our documentation, where she will find more details about
>> possible restrictions in place against ptrace.
>
> Doesn't it worsen user experience compared to the current Fedora
> patch? Now a user first coming across the problem will have to
> navigate the documentation, then check possible scenarios – instead of
> simply getting a friendly explanation what exactly is wrong.

I agree that it is easier to just print the reason for the failure
directly, instead of pointing to the documentation.  However, the reason
I chose the latter is because it may not always be possible to identify
the reason, and this might confuse the user or lead him to wrong
conclusions.  I'm mostly thinking about the seccomp feature here
(because I have no idea how to check if it's active or not), but I'm
also concerned that it's unreasonable to expect users to have
libselinux-devel (on Fedora) installed.

> Compare this with the patch applied by some Linux distributions
> (CentOS IIRC) to make Bash emit
>
>> bash: myprogram: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
>
> when ELF interpreter can't be found. By default, Bash only prints the
> less than helpful message
>
>> bash: myprogram: No such file or directory
>
> Your proposal (if it's intended to replace Fedora's GDB patch) is
> similar to replacing this default with
>
>> bash: myprogram: No such file or directory. There might be other reasons than simply command executable not existing. Please see Bash documentation section XYZ for details.
>
> and adding a section XYZ in Bash docs explaining that the reasons
> could include lacking libraries, broken symlink to the binary, stale
> hash table of PATH lookups, etc.. This is considerably worse than the
> patch with explicit announcement of actual problem.

Well, in this specific case it's probably not difficult to check for the
right cause and print the specific warning.  However, if there were more
than 2 or 3 scenarios where a program couldn't be executed, and if
checking for them involved depending on external libraries and such,
then we'd be talking about a similar situation here.

> So as a user, I disfavor this approach, although it might be more
> maintainable for GDB developers. But, if dependency on libselinux is
> the only objection, then why not simply dlopen it as needed (and
> gracefully handle failure to do so) instead of ELF-level linking to
> it? Then there'll be no hard dependency on libselinux, while user
> experience will not degrade too.

Thanks for expressing your opinion as user, much appreciated!

I had considered dlopen'ing libselinux before, but didn't do that
because of what I mentioned earlier: I think it's not reasonable to
expect that the user will have libselinux-devel installed, and SELinux
is just one of the possible scenarios here.

But your message made me rethink and decide to do the following:

- Try to dlopen libselinux, and check for deny_ptrace if possible.  If
  it works and deny_ptrace is on, emit a specific warning about it.  If
  it doesn't work or if deny_ptrace is off, continue.

- Try to check the contents of /proc/sys/kernel/yama/ptrace_scope.  If
  it works and if ptrace_scope > 0, emit a specific warning about it.
  Otherwise, continue.

- See if I can come up with a way to check the seccomp thing.  If
  possible, emit a specific warning about it.  Otherwise, continue.

- If everything failed, emit a generic warning pointing to the docs.

I hope this is a better approach.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
  2019-08-19  9:10 ` Ruslan Kabatsayev
@ 2019-08-19 14:33 ` Eli Zaretskii
  2019-08-25 22:38   ` Sergio Durigan Junior
  2019-08-19 18:26 ` Pedro Alves
                   ` (4 subsequent siblings)
  6 siblings, 1 reply; 98+ messages in thread
From: Eli Zaretskii @ 2019-08-19 14:33 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: gdb-patches

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Sun, 18 Aug 2019 23:29:18 -0400
> 
> +@node Linux kernel @code{ptrace} restrictions

@-commands in node names are best avoided (here and elsewhere in your
patch).

> +The @code{ptrace} system call is used by @value{GDBN} to, among other
> +things, attach to a new or existing inferior in order to start
> +debugging it.

This sentence should mention Linux, otherwise it's too general.

> +we will expand on how this malfunction can manifest, and how to modify
                                              ^^^^^^^^
"manifest itself"

Also, I see no description of how these problems manifest themselves.
I suggest to show the respective error messages, and also index them,
so that interested readers could find this material quickly.

> +@cindex selinux, deny_ptrace

I suggest to add a @cindex entry for deny_ptrace itself, or maybe
switch the order:

  @cindex deny_ptrace, SELinux

> +If you see anything other than @code{0}, @value{GDBN} can be affected
> +by it.  You can temporarily disable the feature by doing:
> +
> +@smallexample
> +# sysctl kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample

I'm guessing this should be done as root, right?  If so, I think we
should mention that.

Thanks.

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19 13:47   ` Sergio Durigan Junior
@ 2019-08-19 14:57     ` Ruslan Kabatsayev
  2019-08-19 16:30     ` Christian Biesinger via gdb-patches
  1 sibling, 0 replies; 98+ messages in thread
From: Ruslan Kabatsayev @ 2019-08-19 14:57 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches

On Mon, 19 Aug 2019 at 16:47, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
>
> On Monday, August 19 2019, Ruslan Kabatsayev wrote:
>
> > Hello Sergio,
>
> Hey Ruslan,
>
> > On Mon, 19 Aug 2019 at 06:29, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
> >>
> >> In Fedora GDB, we carry the following patch:
> >>
> >>   https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch
> >>
> >> Its purpose is to try to detect a specific scenario where SELinux's
> >> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
> >> order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
> >> with EACCES in this case).
> >>
> >> I like the idea of improving error detection and providing more
> >> information to the user (a simple "Permission denied" can be really
> >> frustrating), but I don't fully agree with the way the patch was
> >> implemented: it makes GDB link against libselinux only for the sake of
> >> consulting the 'deny_ptrace' setting, and then prints a warning if
> >> ptrace failed and this setting is on.
> >>
> >> Instead of printing a very certain warning about a very specific
> >> setting, I decided to propose the following patch, which prints a
> >> generic warning about a possible situation (i.e., without going
> >> bothering to make sure that the situation is actually happening).
> >> What this means is that whenever a ptrace error is detected, and if
> >> errno is either EACCES or EPERM, a warning will be printed pointing
> >> the user to our documentation, where she will find more details about
> >> possible restrictions in place against ptrace.
> >
> > Doesn't it worsen user experience compared to the current Fedora
> > patch? Now a user first coming across the problem will have to
> > navigate the documentation, then check possible scenarios – instead of
> > simply getting a friendly explanation what exactly is wrong.
>
> I agree that it is easier to just print the reason for the failure
> directly, instead of pointing to the documentation.  However, the reason
> I chose the latter is because it may not always be possible to identify
> the reason, and this might confuse the user or lead him to wrong
> conclusions.  I'm mostly thinking about the seccomp feature here
> (because I have no idea how to check if it's active or not), but I'm
> also concerned that it's unreasonable to expect users to have
> libselinux-devel (on Fedora) installed.
>
> > Compare this with the patch applied by some Linux distributions
> > (CentOS IIRC) to make Bash emit
> >
> >> bash: myprogram: /lib/ld-linux.so.2: bad ELF interpreter: No such file or directory
> >
> > when ELF interpreter can't be found. By default, Bash only prints the
> > less than helpful message
> >
> >> bash: myprogram: No such file or directory
> >
> > Your proposal (if it's intended to replace Fedora's GDB patch) is
> > similar to replacing this default with
> >
> >> bash: myprogram: No such file or directory. There might be other reasons than simply command executable not existing. Please see Bash documentation section XYZ for details.
> >
> > and adding a section XYZ in Bash docs explaining that the reasons
> > could include lacking libraries, broken symlink to the binary, stale
> > hash table of PATH lookups, etc.. This is considerably worse than the
> > patch with explicit announcement of actual problem.
>
> Well, in this specific case it's probably not difficult to check for the
> right cause and print the specific warning.  However, if there were more
> than 2 or 3 scenarios where a program couldn't be executed, and if
> checking for them involved depending on external libraries and such,
> then we'd be talking about a similar situation here.
>
> > So as a user, I disfavor this approach, although it might be more
> > maintainable for GDB developers. But, if dependency on libselinux is
> > the only objection, then why not simply dlopen it as needed (and
> > gracefully handle failure to do so) instead of ELF-level linking to
> > it? Then there'll be no hard dependency on libselinux, while user
> > experience will not degrade too.
>
> Thanks for expressing your opinion as user, much appreciated!
>
> I had considered dlopen'ing libselinux before, but didn't do that
> because of what I mentioned earlier: I think it's not reasonable to
> expect that the user will have libselinux-devel installed, and SELinux
> is just one of the possible scenarios here.
>
> But your message made me rethink and decide to do the following:
>
> - Try to dlopen libselinux, and check for deny_ptrace if possible.  If
>   it works and deny_ptrace is on, emit a specific warning about it.  If
>   it doesn't work or if deny_ptrace is off, continue.
>
> - Try to check the contents of /proc/sys/kernel/yama/ptrace_scope.  If
>   it works and if ptrace_scope > 0, emit a specific warning about it.
>   Otherwise, continue.
>
> - See if I can come up with a way to check the seccomp thing.  If
>   possible, emit a specific warning about it.  Otherwise, continue.
>
> - If everything failed, emit a generic warning pointing to the docs.

Yes, I did consider suggesting the same, just thought it would look
too complicated to have both generic and specific cases. I'm glad that
you've come to this solution anyway.

>
> I hope this is a better approach.
>
> Thanks,
>
> --
> Sergio
> GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
> Please send encrypted e-mail if possible
> http://sergiodj.net/

Thanks,
Ruslan

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19 13:47   ` Sergio Durigan Junior
  2019-08-19 14:57     ` Ruslan Kabatsayev
@ 2019-08-19 16:30     ` Christian Biesinger via gdb-patches
  2019-08-19 17:04       ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Christian Biesinger via gdb-patches @ 2019-08-19 16:30 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: Ruslan Kabatsayev, GDB Patches

On Mon, Aug 19, 2019 at 8:47 AM Sergio Durigan Junior
<sergiodj@redhat.com> wrote:
> I'm
> also concerned that it's unreasonable to expect users to have
> libselinux-devel (on Fedora) installed.

I'm confused, why would users have to have the -dev version installed?
Can't you just dlopen("libselinux.so.1") and it'll work fine without
the -dev package?

Christian

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19 16:30     ` Christian Biesinger via gdb-patches
@ 2019-08-19 17:04       ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-19 17:04 UTC (permalink / raw)
  To: Christian Biesinger; +Cc: Ruslan Kabatsayev, GDB Patches

On Monday, August 19 2019, Christian Biesinger wrote:

> On Mon, Aug 19, 2019 at 8:47 AM Sergio Durigan Junior
> <sergiodj@redhat.com> wrote:
>> I'm
>> also concerned that it's unreasonable to expect users to have
>> libselinux-devel (on Fedora) installed.
>
> I'm confused, why would users have to have the -dev version installed?
> Can't you just dlopen("libselinux.so.1") and it'll work fine without
> the -dev package?

libselinux.so is present in the libselinux-devel package, but
libselinux.so.1 is present in the libselinux package.  We could dlopen
it, sure.  I don't know if libselinux is installed by default, though.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
  2019-08-19  9:10 ` Ruslan Kabatsayev
  2019-08-19 14:33 ` Eli Zaretskii
@ 2019-08-19 18:26 ` Pedro Alves
  2019-08-25 22:40   ` Sergio Durigan Junior
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
                   ` (3 subsequent siblings)
  6 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-08-19 18:26 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches

Two comments on implementation, that I think apply 
equally to the new version you're working on as well:

#1 - A target method (target_ptrace_me_fail_reason) looks like the
  wrong place to put this, to me.  As I understand it, the core of
  gdb isn't ever calling the target_ptrace_me_fail_reason.  This is
  all code that is implementation detail of the Linux target
  implementation.

  I'd prefer some other way to address this.  An idea would be to have
  a function pointer in inf-ptrace.c that defaults to an implementation
  that returns the empty string:

   // in inf-ptrace.c

   static std::string
   default_inf_ptrace_me_fail_reason (int err)
   {
     return {};
   }

   std::string (*inf_ptrace_me_fail_reason) (int err) = default_inf_ptrace_me_fail_reason;

   // in inf-ptrace.h

   extern std::string (*inf_ptrace_me_fail_reason) (int err);

   And then in linux-nat.c:_initialize_linux_nat, you'd override the
   implementation, like:

   std::string
   linux_ptrace_me_fail_reason (int err)
   {
     ...
   }

   void
   _initialize_linux_nat ()
   {
      ... 
      inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
   }


  Note I'm not suggesting anything with virtual methods before
  of the next point.

#2 - What about gdbserver?  Don't we want the same nice error
messages in gdbserver?

See gdbserver/linux-low.c:linux_ptrace_fun.

This would suggest putting that linux_ptrace_me_fail_reason
function in gdb/nat/ so that it would be used by gdbserver too.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19 14:33 ` Eli Zaretskii
@ 2019-08-25 22:38   ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-25 22:38 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches

On Monday, August 19 2019, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: Sergio Durigan Junior <sergiodj@redhat.com>
>> Date: Sun, 18 Aug 2019 23:29:18 -0400
>> 
>> +@node Linux kernel @code{ptrace} restrictions
>
> @-commands in node names are best avoided (here and elsewhere in your
> patch).

Fixed.

>> +The @code{ptrace} system call is used by @value{GDBN} to, among other
>> +things, attach to a new or existing inferior in order to start
>> +debugging it.
>
> This sentence should mention Linux, otherwise it's too general.

I thought mentioning Linux in the title would be enough.  I've modified
the sentence to read like this:

  The @code{ptrace} system call is used by @value{GDBN} on Linux to,
  among other things...

>> +we will expand on how this malfunction can manifest, and how to modify
>                                               ^^^^^^^^
> "manifest itself"

Fixed.

> Also, I see no description of how these problems manifest themselves.
> I suggest to show the respective error messages, and also index them,
> so that interested readers could find this material quickly.

As per our recent discussions in the thread, I changed the patch to
actively try to determine the cause of the ptrace failure, and print
descriptive messages for each scenario.  When it isn't able to detect
the root cause, it will print the generic message pointing the user to
our documentation.  Given this change in the original behaviour, I'm
probably going to rewrite this sentence so that it's more clear about
what should be expected.

>> +@cindex selinux, deny_ptrace
>
> I suggest to add a @cindex entry for deny_ptrace itself, or maybe
> switch the order:
>
>   @cindex deny_ptrace, SELinux

Fair enough, I'll add a separate entry for deny_ptrace.

>> +If you see anything other than @code{0}, @value{GDBN} can be affected
>> +by it.  You can temporarily disable the feature by doing:
>> +
>> +@smallexample
>> +# sysctl kernel.yama.ptrace_scope=0
>> +kernel.yama.ptrace_scope = 0
>> +@end smallexample
>
> I'm guessing this should be done as root, right?  If so, I think we
> should mention that.

Yes, this command should be run as root.  I thought that just using "#"
vs. "$" in the prompt would suffice, but you're right, I should be more
explicit.

I'll send a v2 with the fixes soon.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Improve ptrace-error detection on Linux targets
  2019-08-19 18:26 ` Pedro Alves
@ 2019-08-25 22:40   ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-25 22:40 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches

On Monday, August 19 2019, Pedro Alves wrote:

> Two comments on implementation, that I think apply 
> equally to the new version you're working on as well:

Thanks for the review.  I didn't reply before, but I used your
suggestions in the next version.

> #1 - A target method (target_ptrace_me_fail_reason) looks like the
>   wrong place to put this, to me.  As I understand it, the core of
>   gdb isn't ever calling the target_ptrace_me_fail_reason.  This is
>   all code that is implementation detail of the Linux target
>   implementation.
>
>   I'd prefer some other way to address this.  An idea would be to have
>   a function pointer in inf-ptrace.c that defaults to an implementation
>   that returns the empty string:
>
>    // in inf-ptrace.c
>
>    static std::string
>    default_inf_ptrace_me_fail_reason (int err)
>    {
>      return {};
>    }
>
>    std::string (*inf_ptrace_me_fail_reason) (int err) = default_inf_ptrace_me_fail_reason;
>
>    // in inf-ptrace.h
>
>    extern std::string (*inf_ptrace_me_fail_reason) (int err);
>
>    And then in linux-nat.c:_initialize_linux_nat, you'd override the
>    implementation, like:
>
>    std::string
>    linux_ptrace_me_fail_reason (int err)
>    {
>      ...
>    }
>
>    void
>    _initialize_linux_nat ()
>    {
>       ... 
>       inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
>    }
>
>
>   Note I'm not suggesting anything with virtual methods before
>   of the next point.
>
> #2 - What about gdbserver?  Don't we want the same nice error
> messages in gdbserver?
>
> See gdbserver/linux-low.c:linux_ptrace_fun.
>
> This would suggest putting that linux_ptrace_me_fail_reason
> function in gdb/nat/ so that it would be used by gdbserver too.

Thanks.  I followed both of your suggestions, and will submit a v2 soon.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
                   ` (2 preceding siblings ...)
  2019-08-19 18:26 ` Pedro Alves
@ 2019-08-26 18:32 ` Sergio Durigan Junior
  2019-08-26 18:35   ` Christian Biesinger via gdb-patches
                     ` (3 more replies)
  2019-09-04 19:54 ` [PATCH v3] " Sergio Durigan Junior
                   ` (2 subsequent siblings)
  6 siblings, 4 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-26 18:32 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev, Sergio Durigan Junior

Changes from v1:

- Addressed Pedro's comments re. internal organization and gdbserver
  support.

- Addressed Eli's comments (doc fixes).

- New ways of detecting what's wrong with ptrace.



In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

My first thought (and attempt) was to make GDB print a generic warning
when a ptrace error happened; this message would just point the user
to our documentation, where she could find more information about
possible causes for the error (and try to diagnose/fix the problem).
This proved to be too simple, and I was convinced that it is actually
a good idea to go the extra kilometre and try to pinpoint the specific
problem (or problems) preventing ptrace from working, as well as
provide useful suggestions on how the user can fix things.

Here is the patch I came up with.  It implements a new function,
'linux_ptrace_restricted_fail_reason', which does a few things to
check what's wrong with ptrace:

  - It dlopen's "libselinux.so" and checks if the "deny_ptrace" option
    is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  Error:
  warning: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  Please, disable it by executing (as root):

    setsebool deny_ptrace off

  During startup program exited with code 127.

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  Error:
  warning: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  Please, disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  During startup program exited with code 127.

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  gdbserver: Could not trace the inferior process.
  Error:
  gdbserver: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  Please, disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  linux_check_ptrace_features: waitpid: unexpected status 32512.
  Exiting

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.

This means that the patch expands our documentation and creates a new
appendix section named "Linux kernel ptrace restrictions", with
sub-sections for each possible restriction that might be in place.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

I tested this patch locally, on my Fedora 30 machine (actually, a
Fedora Rawhide VM), but I'm not proposing a testcase for it because of
the difficulty of writing one.

WDYT?

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>

	* gdbsupport/gdb-dlfcn.c (gdb_dlopen): Add argument 'dont_throw'
	Don't throw if it's true.
	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Add optional argument
	'dont_throw'.  Update comment.
	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
	function.
	(inf_ptrace_me_fail_reason): New variable.
	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
	* linux-nat.c (linux_nat_target::attach): Update call to
	'linux_ptrace_attach_fail_reason'.
	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
	* linux-nat.h (linux_nat_target) <ptrace_me_fail_reason>: New
	method.
	* nat/fork-inferior.c (trace_start_error_with_name): Add
	optional 'append' argument.
	* nat/fork-inferior.h (trace_start_error_with_name): Update
	prototype.
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h" and
	"nat/fork-inferior.h".
	(selinux_ftype): New typedef.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason): Add optional 'err'
	argument.  Call 'linux_ptrace_restricted_fail_reason'.
	(linux_ptrace_me_fail_reason): New function.
	(linux_child_function): Handle ptrace error.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
	prototype.
	(linux_ptrace_me_fail_reason): New function.
	* target-delegates.c: Regenerate.
	* target.h (struct target_ops) <ptrace_me_fail_reason>: New
	method.
	(target_ptrace_me_fail_reason): New define.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* linux-low.c (linux_ptrace_fun): Call
	'linux_ptrace_me_fail_reason'.
---
 gdb/doc/gdb.texinfo        | 138 +++++++++++++++++++++++++++++++++++++
 gdb/gdbserver/linux-low.c  |   3 +-
 gdb/gdbsupport/gdb-dlfcn.c |   4 +-
 gdb/gdbsupport/gdb-dlfcn.h |   7 +-
 gdb/inf-ptrace.c           |  19 ++++-
 gdb/inf-ptrace.h           |  10 +++
 gdb/linux-nat.c            |   7 +-
 gdb/nat/fork-inferior.c    |   4 +-
 gdb/nat/fork-inferior.h    |   7 +-
 gdb/nat/linux-ptrace.c     |  86 +++++++++++++++++++++--
 gdb/nat/linux-ptrace.h     |  15 +++-
 11 files changed, 282 insertions(+), 18 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index e1bc8143e6..43f749c6f1 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel @code{ptrace} restrictions:: Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -44682,6 +44685,141 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} on GNU/Linux to,
+among other things, attach to a new or existing inferior in order to
+start debugging it.  Due to security concerns, some distributions and
+vendors disable or severily restrict the ability to perform these
+operations, which can make @value{GDBN} malfunction.  In this section,
+we will expand on how this malfunction can manifest itself, and how to
+modify the system's settings in order to be able to use @value{GDBN}
+properly.
+
+@menu
+* The GDB error message::               The error message displayed when the
+                                        system prevents @value{GDBN} from using
+                                        @code{ptrace}
+* SELinux's @code{deny_ptrace}::        SELinux and the @code{deny_ptrace} option
+* Yama's @code{ptrace_scope}::          Yama and the @code{ptrace_scope} setting
+* Docker and @code{seccomp}::           Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The GDB error message
+@appendixsection The @value{GDBN} error message
+
+When the system prevents @value{GDBN} from using the @code{ptrace}
+system call, you will likely see a descriptive error message
+explaining what is wrong and how to attempt to fix the problem.  For
+example, when SELinux's @code{deny_ptrace} option is enabled, you can
+see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing GDB
+from using 'ptrace'.  Please, disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} can be affected
+by it.  You can temporarily disable the feature by doing, as root:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN}.  To do that, you can use the
+options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
+when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3113017ae6..1e0a5cbf54 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -971,7 +971,8 @@ linux_ptrace_fun ()
 {
   if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
 	      (PTRACE_TYPE_ARG4) 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 linux_ptrace_me_fail_reason (errno).c_str ());
 
   if (setpgid (0, 0) < 0)
     trace_start_error_with_name ("setpgid");
diff --git a/gdb/gdbsupport/gdb-dlfcn.c b/gdb/gdbsupport/gdb-dlfcn.c
index 921f10f3d8..9e5a992c17 100644
--- a/gdb/gdbsupport/gdb-dlfcn.c
+++ b/gdb/gdbsupport/gdb-dlfcn.c
@@ -58,7 +58,7 @@ is_dl_available (void)
 #else /* NO_SHARED_LIB */
 
 gdb_dlhandle_up
-gdb_dlopen (const char *filename)
+gdb_dlopen (const char *filename, bool dont_throw)
 {
   void *result;
 #ifdef HAVE_DLFCN_H
@@ -66,7 +66,7 @@ gdb_dlopen (const char *filename)
 #elif __MINGW32__
   result = (void *) LoadLibrary (filename);
 #endif
-  if (result != NULL)
+  if (dont_throw || result != NULL)
     return gdb_dlhandle_up (result);
 
 #ifdef HAVE_DLFCN_H
diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
index 6a39d38941..a8ddbc03da 100644
--- a/gdb/gdbsupport/gdb-dlfcn.h
+++ b/gdb/gdbsupport/gdb-dlfcn.h
@@ -32,10 +32,11 @@ struct dlclose_deleter
 typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
 
 /* Load the dynamic library file named FILENAME, and return a handle
-   for that dynamic library.  Return NULL if the loading fails for any
-   reason.  */
+   for that dynamic library.  If the loading fails, return NULL if
+   DONT_THROW is true, or throw an exception otherwise (default
+   behaviour).  */
 
-gdb_dlhandle_up gdb_dlopen (const char *filename);
+gdb_dlhandle_up gdb_dlopen (const char *filename, bool dont_throw = false);
 
 /* Return the address of the symbol named SYMBOL inside the shared
    library whose handle is HANDLE.  Return NULL when the symbol could
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 4a8e732373..b6cfd803cf 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -94,6 +94,22 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 #endif /* PT_GET_PROCESS_STATE */
 \f
 
+/* Default method for "inf_ptrace_me_fail_reason", which returns an
+   empty string.  */
+
+static std::string
+default_inf_ptrace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* Point to "inf_ptrace_me_fail_reason", which implements a function
+   that can be called by "inf_ptrace_me" in order to obtain the reason
+   for failure.  */
+
+std::string (*inf_ptrace_me_fail_reason) (int err)
+  = default_inf_ptrace_me_fail_reason;
+
 /* Prepare to be traced.  */
 
 static void
@@ -101,7 +117,8 @@ inf_ptrace_me (void)
 {
   /* "Trace me, Dr. Memory!"  */
   if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 inf_ptrace_me_fail_reason (errno).c_str ());
 }
 
 /* Start a new inferior Unix child process.  EXEC_FILE is the file to
diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
index 98b5d2e09e..7cdab9af89 100644
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -83,4 +83,14 @@ protected:
 
 extern pid_t get_ptrace_pid (ptid_t);
 
+/* Pointer to "inf_ptrace_me_fail_reason", which implements a function
+   that can be called by "inf_ptrace_me" in order to obtain the reason
+   for a ptrace failure.  ERR is the ERRNO value set by the failing
+   ptrace call.
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when ptrace fails (see linux-nat.c, for
+   example).  */
+extern std::string (*inf_ptrace_me_fail_reason) (int err);
+
 #endif
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 945c19f666..b5a9eaf72e 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1191,8 +1191,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
@@ -4696,6 +4697,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 68b51aa814..72ac623e20 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index 1d0519fb26..7e6b889210 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
 #endif /* NAT_FORK_INFERIOR_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index c1ebc0a032..599d9cfb55 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,8 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -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);
+
+  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

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
@ 2019-08-26 18:35   ` Christian Biesinger via gdb-patches
  2019-08-26 20:51     ` Sergio Durigan Junior
  2019-08-26 18:44   ` Eli Zaretskii
                     ` (2 subsequent siblings)
  3 siblings, 1 reply; 98+ messages in thread
From: Christian Biesinger via gdb-patches @ 2019-08-26 18:35 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

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
>

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
  2019-08-26 18:35   ` Christian Biesinger via gdb-patches
@ 2019-08-26 18:44   ` Eli Zaretskii
  2019-08-29 14:40   ` Pedro Alves
  2019-08-30 12:47   ` Pedro Alves
  3 siblings, 0 replies; 98+ messages in thread
From: Eli Zaretskii @ 2019-08-26 18:44 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: gdb-patches, palves, b7.10110111

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Ruslan Kabatsayev <b7.10110111@gmail.com>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Mon, 26 Aug 2019 14:32:05 -0400
> 
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
> 	section.

OK for this part.

Thanks.

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-26 18:35   ` Christian Biesinger via gdb-patches
@ 2019-08-26 20:51     ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-26 20:51 UTC (permalink / raw)
  To: Christian Biesinger
  Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Monday, August 26 2019, Christian Biesinger wrote:

> 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.

Ah, right, I forgot about this.  I fixed it in my local copy, thanks for
pointing it out.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
  2019-08-26 18:35   ` Christian Biesinger via gdb-patches
  2019-08-26 18:44   ` Eli Zaretskii
@ 2019-08-29 14:40   ` Pedro Alves
  2019-08-29 19:27     ` Sergio Durigan Junior
  2019-08-30 12:47   ` Pedro Alves
  3 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-08-29 14:40 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches; +Cc: Eli Zaretskii, Ruslan Kabatsayev

Hi Sergio,

This is looking quite good to me.  Some comments below.

On 8/26/19 7:32 PM, Sergio Durigan Junior wrote:
> Changes from v1:
> 
> - Addressed Pedro's comments re. internal organization and gdbserver
>   support.
> 
> - Addressed Eli's comments (doc fixes).
> 
> - New ways of detecting what's wrong with ptrace.
> 
> 
> 
> In Fedora GDB, we carry the following patch:
> 
>   https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch

This link will soon be dead, right?  (thinking about the commit log)
Maybe point at some reference other than master.

> Its purpose is to try to detect a specific scenario where SELinux's
> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
> order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
> with EACCES in this case).
> 
> I like the idea of improving error detection and providing more
> information to the user (a simple "Permission denied" can be really
> frustrating), but I don't fully agree with the way the patch was
> implemented: it makes GDB link against libselinux only for the sake of
> consulting the 'deny_ptrace' setting, and then prints a warning if
> ptrace failed and this setting is on.
> 
> My first thought (and attempt) was to make GDB print a generic warning
> when a ptrace error happened; this message would just point the user
> to our documentation, where she could find more information about
> possible causes for the error (and try to diagnose/fix the problem).
> This proved to be too simple, and I was convinced that it is actually
> a good idea to go the extra kilometre and try to pinpoint the specific
> problem (or problems) preventing ptrace from working, as well as
> provide useful suggestions on how the user can fix things.
> 
> Here is the patch I came up with.  It implements a new function,
> 'linux_ptrace_restricted_fail_reason', which does a few things to
> check what's wrong with ptrace:
> 
>   - It dlopen's "libselinux.so" and checks if the "deny_ptrace" option
>     is enabled.

Update reference to "libselinux.so.1" here too.

> 
>   - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
>     checks if it's different than 0.
> 
> For each of these checks, if it succeeds, the user will see a message
> informing about the restriction in place, and how it can be disabled.
> For example, if "deny_ptrace" is enabled, the user will see:
> 
>   # gdb /bin/true
>   ...
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   Error:
>   warning: ptrace: Permission denied

Curious, that:

 warning:
 Error:
 warning:

looks a bit odd, specifically the "Error:" line.  Do you know where is
that coming from?

>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>   from using 'ptrace'.  Please, disable it by executing (as root):

I'm really not a fan of these "Please, ".  Kind of sounds like
gdb is begging, to me.  I'd rather use an informational tone, like:

   The SELinux 'deny_ptrace' option is enabled and preventing GDB
   from using 'ptrace'.  You can disable it by executing (as root):

> 
>     setsebool deny_ptrace off
> 
>   During startup program exited with code 127.
> 
> In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:
> 
>   # gdb /bin/true
>   ...
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   Error:
>   warning: ptrace: Operation not permitted
>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>   GDB from using 'ptrace'.  Please, disable it by executing (as root):

Ditto.

> 
>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
> 
>   During startup program exited with code 127.
> 
> If both restrictions are enabled, both messages will show up.
> 
> This works for gdbserver as well, and actually fixes a latent bug I
> found: when ptrace is restricted, gdbserver would hang due to an
> unchecked ptrace call:
> 
>   # gdbserver :9988 /bin/true
>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
>   [ Here you would have to issue a C-c ]
> 
> Now, you will see:
> 
>   # gdbserver :9988 /bin/true
>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
>   gdbserver: Could not trace the inferior process.
>   Error:
>   gdbserver: ptrace: Operation not permitted
>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>   GDB from using 'ptrace'.  Please, disable it by executing (as root):
> 
>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
> 
>   linux_check_ptrace_features: waitpid: unexpected status 32512.
>   Exiting
> 
> (I decided to keep all the other messages, even though I find them a
> bit distracting).

Yeah, the other messages are implementor-speak, showing gdb function
names.  We should ideally clean this all up.

> 
> If GDB can't determine the cause for the failure, it will still print
> the generic error message which tells the user to check our
> documentation:
> 
>   There might be restrictions preventing ptrace from working.  Please see
>   the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>   for more details.
> 
> This means that the patch expands our documentation and creates a new
> appendix section named "Linux kernel ptrace restrictions", with
> sub-sections for each possible restriction that might be in place.
> 
> The current list of possible restrictions is:
> 
>   - SELinux's 'deny_ptrace' option (detected).
> 
>   - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).
> 
>   - seccomp on Docker containers (I couldn't find how to detect).
> 
> It's important to mention that all of this is Linux-specific; as far
> as I know, SELinux, YAMA and seccomp are Linux-only features.
> 
> I tested this patch locally, on my Fedora 30 machine (actually, a
> Fedora Rawhide VM), but I'm not proposing a testcase for it because of
> the difficulty of writing one.
> 
> WDYT?
> 
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
> 	section.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
> 
> 	* gdbsupport/gdb-dlfcn.c (gdb_dlopen): Add argument 'dont_throw'
> 	Don't throw if it's true.
> 	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Add optional argument
> 	'dont_throw'.  Update comment.
> 	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
> 	function.
> 	(inf_ptrace_me_fail_reason): New variable.
> 	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
> 	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
> 	* linux-nat.c (linux_nat_target::attach): Update call to
> 	'linux_ptrace_attach_fail_reason'.
> 	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
> 	* linux-nat.h (linux_nat_target) <ptrace_me_fail_reason>: New
> 	method.
> 	* nat/fork-inferior.c (trace_start_error_with_name): Add
> 	optional 'append' argument.
> 	* nat/fork-inferior.h (trace_start_error_with_name): Update
> 	prototype.
> 	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h" and
> 	"nat/fork-inferior.h".
> 	(selinux_ftype): New typedef.
> 	(linux_ptrace_restricted_fail_reason): New function.
> 	(linux_ptrace_attach_fail_reason): Add optional 'err'
> 	argument.  Call 'linux_ptrace_restricted_fail_reason'.
> 	(linux_ptrace_me_fail_reason): New function.
> 	(linux_child_function): Handle ptrace error.
> 	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
> 	prototype.
> 	(linux_ptrace_me_fail_reason): New function.
> 	* target-delegates.c: Regenerate.
> 	* target.h (struct target_ops) <ptrace_me_fail_reason>: New
> 	method.
> 	(target_ptrace_me_fail_reason): New define.
> 
> gdb/gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* linux-low.c (linux_ptrace_fun): Call
> 	'linux_ptrace_me_fail_reason'.
> ---
>  gdb/doc/gdb.texinfo        | 138 +++++++++++++++++++++++++++++++++++++
>  gdb/gdbserver/linux-low.c  |   3 +-
>  gdb/gdbsupport/gdb-dlfcn.c |   4 +-
>  gdb/gdbsupport/gdb-dlfcn.h |   7 +-
>  gdb/inf-ptrace.c           |  19 ++++-
>  gdb/inf-ptrace.h           |  10 +++
>  gdb/linux-nat.c            |   7 +-
>  gdb/nat/fork-inferior.c    |   4 +-
>  gdb/nat/fork-inferior.h    |   7 +-
>  gdb/nat/linux-ptrace.c     |  86 +++++++++++++++++++++--
>  gdb/nat/linux-ptrace.h     |  15 +++-
>  11 files changed, 282 insertions(+), 18 deletions(-)
> 
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index e1bc8143e6..43f749c6f1 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -182,6 +182,9 @@ software in general.  We will miss him.
>                                  @value{GDBN}
>  * Operating System Information:: Getting additional information from
>                                   the operating system
> +* Linux kernel @code{ptrace} restrictions:: Restrictions sometimes
> +                                            imposed by the Linux
> +                                            kernel on @code{ptrace}
>  * Trace File Format::		GDB trace file format
>  * Index Section Format::        .gdb_index section format
>  * Man Pages::			Manual pages
> @@ -44682,6 +44685,141 @@ should contain a comma-separated list of cores that this process
>  is running on.  Target may provide additional columns,
>  which @value{GDBN} currently ignores.
>  
> +@node Linux kernel ptrace restrictions
> +@appendix Linux kernel @code{ptrace} restrictions
> +@cindex linux kernel ptrace restrictions, attach
> +
> +The @code{ptrace} system call is used by @value{GDBN} on GNU/Linux to,
> +among other things, attach to a new or existing inferior in order to
> +start debugging it.  Due to security concerns, some distributions and
> +vendors disable or severily restrict the ability to perform these

typo: severily -> severely


> +operations, which can make @value{GDBN} malfunction.  In this section,
> +we will expand on how this malfunction can manifest itself, and how to
> +modify the system's settings in order to be able to use @value{GDBN}
> +properly.
> +
> +@menu
> +* The GDB error message::               The error message displayed when the
> +                                        system prevents @value{GDBN} from using
> +                                        @code{ptrace}
> +* SELinux's @code{deny_ptrace}::        SELinux and the @code{deny_ptrace} option
> +* Yama's @code{ptrace_scope}::          Yama and the @code{ptrace_scope} setting
> +* Docker and @code{seccomp}::           Docker and the @code{seccomp}
> +                                        infrastructure
> +@end menu
> +
> +@node The GDB error message
> +@appendixsection The @value{GDBN} error message
> +
> +When the system prevents @value{GDBN} from using the @code{ptrace}
> +system call, you will likely see a descriptive error message
> +explaining what is wrong and how to attempt to fix the problem.  For
> +example, when SELinux's @code{deny_ptrace} option is enabled, you can
> +see:
> +
> +@smallexample
> +$ gdb program
> +...
> +(@value{GDBP}) run
> +Starting program: program
> +warning: Could not trace the inferior process.
> +Error:
> +warning: ptrace: Permission denied
> +The SELinux 'deny_ptrace' option is enabled and preventing GDB
> +from using 'ptrace'.  Please, disable it by executing (as root):
> +
> +  setsebool deny_ptrace off
> +
> +During startup program exited with code 127.
> +(@value{GDBP})
> +@end smallexample
> +
> +Sometimes, it may not be possible to acquire the necessary data to
> +determine the root cause of the failure.  In this case, you will see a
> +generic error message pointing you to this section:
> +
> +@smallexample
> +$ gdb program
> +...
> +Starting program: program
> +warning: Could not trace the inferior process.
> +Error:
> +warning: ptrace: Permission denied
> +There might be restrictions preventing ptrace from working.  Please see
> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
> +for more details.
> +During startup program exited with code 127.
> +(@value{GDBP})
> +@end smallexample
> +
> +@node SELinux's deny_ptrace
> +@appendixsection SELinux's @code{deny_ptrace}
> +@cindex SELinux
> +@cindex deny_ptrace
> +
> +If you are using SELinux, you might want to check whether the
> +@code{deny_ptrace} option is enabled by doing:
> +
> +@smallexample
> +$ getsebool deny_ptrace
> +deny_ptrace --> on
> +@end smallexample
> +
> +If the option is enabled, you can disable it by doing, as root:
> +
> +@smallexample
> +# setsebool deny_ptrace off
> +@end smallexample
> +
> +The option will be disabled until the next reboot.  If you would like
> +to disable it permanently, you can do (as root):
> +
> +@smallexample
> +# setsebool -P deny_ptrace off
> +@end smallexample
> +
> +@node Yama's ptrace_scope
> +@appendixsection Yama's @code{ptrace_scope}
> +@cindex yama, ptrace_scope
> +
> +If your system has Yama enabled, you might want to check whether the
> +@code{ptrace_scope} setting is enabled by checking the value of
> +@file{/proc/sys/kernel/yama/ptrace_scope}:
> +
> +@smallexample
> +$ cat /proc/sys/kernel/yama/ptrace_scope
> +0
> +@end smallexample
> +
> +If you see anything other than @code{0}, @value{GDBN} can be affected
> +by it.  You can temporarily disable the feature by doing, as root:
> +
> +@smallexample
> +# sysctl kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample
> +
> +You can make this permanent by doing, as root:
> +
> +@smallexample
> +# sysctl -w kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample
> +
> +@node Docker and seccomp
> +@appendixsection Docker and @code{seccomp}
> +@cindex docker, seccomp
> +
> +If you are using Docker (@uref{https://www.docker.com/}) containers,
> +you will probably have to disable its @code{seccomp} protections in
> +order to be able to use @value{GDBN}.  To do that, you can use the
> +options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
> +when invoking Docker:
> +
> +@smallexample
> +$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
> +@end smallexample
> +
>  @node Trace File Format
>  @appendix Trace File Format
>  @cindex trace file format
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 3113017ae6..1e0a5cbf54 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -971,7 +971,8 @@ linux_ptrace_fun ()
>  {
>    if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
>  	      (PTRACE_TYPE_ARG4) 0) < 0)
> -    trace_start_error_with_name ("ptrace");
> +    trace_start_error_with_name ("ptrace",
> +				 linux_ptrace_me_fail_reason (errno).c_str ());
>  
>    if (setpgid (0, 0) < 0)
>      trace_start_error_with_name ("setpgid");
> diff --git a/gdb/gdbsupport/gdb-dlfcn.c b/gdb/gdbsupport/gdb-dlfcn.c
> index 921f10f3d8..9e5a992c17 100644
> --- a/gdb/gdbsupport/gdb-dlfcn.c
> +++ b/gdb/gdbsupport/gdb-dlfcn.c
> @@ -58,7 +58,7 @@ is_dl_available (void)
>  #else /* NO_SHARED_LIB */
>  
>  gdb_dlhandle_up
> -gdb_dlopen (const char *filename)
> +gdb_dlopen (const char *filename, bool dont_throw)
>  {
>    void *result;
>  #ifdef HAVE_DLFCN_H
> @@ -66,7 +66,7 @@ gdb_dlopen (const char *filename)
>  #elif __MINGW32__
>    result = (void *) LoadLibrary (filename);
>  #endif
> -  if (result != NULL)
> +  if (dont_throw || result != NULL)
>      return gdb_dlhandle_up (result);
>  
>  #ifdef HAVE_DLFCN_H
> diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
> index 6a39d38941..a8ddbc03da 100644
> --- a/gdb/gdbsupport/gdb-dlfcn.h
> +++ b/gdb/gdbsupport/gdb-dlfcn.h
> @@ -32,10 +32,11 @@ struct dlclose_deleter
>  typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
>  
>  /* Load the dynamic library file named FILENAME, and return a handle
> -   for that dynamic library.  Return NULL if the loading fails for any
> -   reason.  */
> +   for that dynamic library.  If the loading fails, return NULL if
> +   DONT_THROW is true, or throw an exception otherwise (default
> +   behaviour).  */
>  
> -gdb_dlhandle_up gdb_dlopen (const char *filename);
> +gdb_dlhandle_up gdb_dlopen (const char *filename, bool dont_throw = false);
>  
>  /* Return the address of the symbol named SYMBOL inside the shared
>     library whose handle is HANDLE.  Return NULL when the symbol could
> diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
> index 4a8e732373..b6cfd803cf 100644
> --- a/gdb/inf-ptrace.c
> +++ b/gdb/inf-ptrace.c
> @@ -94,6 +94,22 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
>  #endif /* PT_GET_PROCESS_STATE */
>  \f
>  
> +/* Default method for "inf_ptrace_me_fail_reason", which returns an
> +   empty string.  */
> +
> +static std::string
> +default_inf_ptrace_me_fail_reason (int err)
> +{
> +  return {};
> +}
> +
> +/* Point to "inf_ptrace_me_fail_reason", 

Did you mean "Pointer"?  This reads strange because 
"inf_ptrace_me_fail_reason" is the name of the pointer itself,
so how to point _to_ it?

which implements a function
> +   that can be called by "inf_ptrace_me" in order to obtain the reason
> +   for failure.  */
> +
> +std::string (*inf_ptrace_me_fail_reason) (int err)
> +  = default_inf_ptrace_me_fail_reason;
> +
>  /* Prepare to be traced.  */
>  
>  static void
> @@ -101,7 +117,8 @@ inf_ptrace_me (void)
>  {
>    /* "Trace me, Dr. Memory!"  */
>    if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
> -    trace_start_error_with_name ("ptrace");
> +    trace_start_error_with_name ("ptrace",
> +				 inf_ptrace_me_fail_reason (errno).c_str ());
>  }
>  
>  /* Start a new inferior Unix child process.  EXEC_FILE is the file to
> diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
> index 98b5d2e09e..7cdab9af89 100644
> --- a/gdb/inf-ptrace.h
> +++ b/gdb/inf-ptrace.h
> @@ -83,4 +83,14 @@ protected:
>  
>  extern pid_t get_ptrace_pid (ptid_t);
>  
> +/* Pointer to "inf_ptrace_me_fail_reason", which implements a function

Ah, here it's "pointer".  But still reads strange.  I think the comment
in the .c file should just say the usual "See inf-ptrace.h.".

> +   that can be called by "inf_ptrace_me" in order to obtain the reason
> +   for a ptrace failure.  ERR is the ERRNO value set by the failing
> +   ptrace call.
> +
> +   This pointer can be overriden by targets that want to personalize
> +   the error message printed when ptrace fails (see linux-nat.c, for
> +   example).  */
> +extern std::string (*inf_ptrace_me_fail_reason) (int err);
> +
>  #endif
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index 945c19f666..b5a9eaf72e 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -1191,8 +1191,9 @@ linux_nat_target::attach (const char *args, int from_tty)
>      }
>    catch (const gdb_exception_error &ex)
>      {
> +      int saved_errno = errno;
>        pid_t pid = parse_pid_to_attach (args);
> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>  
>        if (!reason.empty ())
>  	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
> @@ -4696,6 +4697,10 @@ Enables printf debugging output."),
>    sigemptyset (&blocked_mask);
>  
>    lwp_lwpid_htab_create ();
> +
> +  /* Set the proper function to generate a message when ptrace
> +     fails.  */
> +  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
>  }
>  \f
>  
> diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
> index 68b51aa814..72ac623e20 100644
> --- a/gdb/nat/fork-inferior.c
> +++ b/gdb/nat/fork-inferior.c
> @@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
>  /* See nat/fork-inferior.h.  */
>  
>  void
> -trace_start_error_with_name (const char *string)
> +trace_start_error_with_name (const char *string, const char *append)
>  {
> -  trace_start_error ("%s: %s", string, safe_strerror (errno));
> +  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
>  }
> diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
> index 1d0519fb26..7e6b889210 100644
> --- a/gdb/nat/fork-inferior.h
> +++ b/gdb/nat/fork-inferior.h
> @@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
>    ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
>  
>  /* Like "trace_start_error", but the error message is constructed by
> -   combining STRING with the system error message for errno.  This
> -   function does not return.  */
> -extern void trace_start_error_with_name (const char *string)
> +   combining STRING with the system error message for errno, and
> +   (optionally) with APPEND.  This function does not return.  */
> +extern void trace_start_error_with_name (const char *string,
> +					 const char *append = "")
>    ATTRIBUTE_NORETURN;
>  
>  #endif /* NAT_FORK_INFERIOR_H */
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index c1ebc0a032..599d9cfb55 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -21,6 +21,8 @@
>  #include "linux-procfs.h"
>  #include "linux-waitpid.h"
>  #include "gdbsupport/buffer.h"
> +#include "gdbsupport/gdb-dlfcn.h"
> +#include "nat/fork-inferior.h"
>  #ifdef HAVE_SYS_PROCFS_H
>  #include <sys/procfs.h>
>  #endif
> @@ -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);
> +
> +  if (handle.get () != NULL)

No need for .get() here.  This:

  if (handle != NULL)

works the same.

(I'd write nullptr throughout instead of NULL in new code.)

> +    {
> +      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");
> +

Use gdb_fopen_cloexec ?

> +  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);

If ERR is an errno number, then it's a bit odd to use -1 for default,
since errno == 0 is the traditional "no error" number.  Pedantically, I believe
there's no garantee that a valid error number must be a positive integer.

But, why the default argument in the first place?  What calls this
without passing an error?

>  
>  /* 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);
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-29 14:40   ` Pedro Alves
@ 2019-08-29 19:27     ` Sergio Durigan Junior
  2019-08-29 19:48       ` Sergio Durigan Junior
  2019-08-30 12:45       ` [PATCH v2] Improve ptrace-error detection on Linux targets Pedro Alves
  0 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-29 19:27 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, August 29 2019, Pedro Alves wrote:

> Hi Sergio,
>
> This is looking quite good to me.  Some comments below.

Thanks for the review, Pedro.

> On 8/26/19 7:32 PM, Sergio Durigan Junior wrote:
>> Changes from v1:
>> 
>> - Addressed Pedro's comments re. internal organization and gdbserver
>>   support.
>> 
>> - Addressed Eli's comments (doc fixes).
>> 
>> - New ways of detecting what's wrong with ptrace.
>> 
>> 
>> 
>> In Fedora GDB, we carry the following patch:
>> 
>>   https://src.fedoraproject.org/rpms/gdb/blob/master/f/gdb-attach-fail-reasons-5of5.patch
>
> This link will soon be dead, right?  (thinking about the commit log)
> Maybe point at some reference other than master.

Yeah.  I will provide a link to a commit.

>> Its purpose is to try to detect a specific scenario where SELinux's
>> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
>> order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
>> with EACCES in this case).
>> 
>> I like the idea of improving error detection and providing more
>> information to the user (a simple "Permission denied" can be really
>> frustrating), but I don't fully agree with the way the patch was
>> implemented: it makes GDB link against libselinux only for the sake of
>> consulting the 'deny_ptrace' setting, and then prints a warning if
>> ptrace failed and this setting is on.
>> 
>> My first thought (and attempt) was to make GDB print a generic warning
>> when a ptrace error happened; this message would just point the user
>> to our documentation, where she could find more information about
>> possible causes for the error (and try to diagnose/fix the problem).
>> This proved to be too simple, and I was convinced that it is actually
>> a good idea to go the extra kilometre and try to pinpoint the specific
>> problem (or problems) preventing ptrace from working, as well as
>> provide useful suggestions on how the user can fix things.
>> 
>> Here is the patch I came up with.  It implements a new function,
>> 'linux_ptrace_restricted_fail_reason', which does a few things to
>> check what's wrong with ptrace:
>> 
>>   - It dlopen's "libselinux.so" and checks if the "deny_ptrace" option
>>     is enabled.
>
> Update reference to "libselinux.so.1" here too.

Done.

>> 
>>   - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
>>     checks if it's different than 0.
>> 
>> For each of these checks, if it succeeds, the user will see a message
>> informing about the restriction in place, and how it can be disabled.
>> For example, if "deny_ptrace" is enabled, the user will see:
>> 
>>   # gdb /bin/true
>>   ...
>>   Starting program: /usr/bin/true
>>   warning: Could not trace the inferior process.
>>   Error:
>>   warning: ptrace: Permission denied
>
> Curious, that:
>
>  warning:
>  Error:
>  warning:
>
> looks a bit odd, specifically the "Error:" line.  Do you know where is
> that coming from?

Not offhand; I'll investigate and come back with the results.

>>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>>   from using 'ptrace'.  Please, disable it by executing (as root):
>
> I'm really not a fan of these "Please, ".  Kind of sounds like
> gdb is begging, to me.  I'd rather use an informational tone, like:
>
>    The SELinux 'deny_ptrace' option is enabled and preventing GDB
>    from using 'ptrace'.  You can disable it by executing (as root):

Fair enough.  Changed as requested.

>> 
>>     setsebool deny_ptrace off
>> 
>>   During startup program exited with code 127.
>> 
>> In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:
>> 
>>   # gdb /bin/true
>>   ...
>>   Starting program: /usr/bin/true
>>   warning: Could not trace the inferior process.
>>   Error:
>>   warning: ptrace: Operation not permitted
>>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>>   GDB from using 'ptrace'.  Please, disable it by executing (as root):
>
> Ditto.

Changed.

>>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
>> 
>>   During startup program exited with code 127.
>> 
>> If both restrictions are enabled, both messages will show up.
>> 
>> This works for gdbserver as well, and actually fixes a latent bug I
>> found: when ptrace is restricted, gdbserver would hang due to an
>> unchecked ptrace call:
>> 
>>   # gdbserver :9988 /bin/true
>>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
>>   [ Here you would have to issue a C-c ]
>> 
>> Now, you will see:
>> 
>>   # gdbserver :9988 /bin/true
>>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
>>   gdbserver: Could not trace the inferior process.
>>   Error:
>>   gdbserver: ptrace: Operation not permitted
>>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>>   GDB from using 'ptrace'.  Please, disable it by executing (as root):
>> 
>>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
>> 
>>   linux_check_ptrace_features: waitpid: unexpected status 32512.
>>   Exiting
>> 
>> (I decided to keep all the other messages, even though I find them a
>> bit distracting).
>
> Yeah, the other messages are implementor-speak, showing gdb function
> names.  We should ideally clean this all up.

Agreed.  I can propose a patch later to clean them.

>> 
>> If GDB can't determine the cause for the failure, it will still print
>> the generic error message which tells the user to check our
>> documentation:
>> 
>>   There might be restrictions preventing ptrace from working.  Please see
>>   the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>>   for more details.
>> 
>> This means that the patch expands our documentation and creates a new
>> appendix section named "Linux kernel ptrace restrictions", with
>> sub-sections for each possible restriction that might be in place.
>> 
>> The current list of possible restrictions is:
>> 
>>   - SELinux's 'deny_ptrace' option (detected).
>> 
>>   - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).
>> 
>>   - seccomp on Docker containers (I couldn't find how to detect).
>> 
>> It's important to mention that all of this is Linux-specific; as far
>> as I know, SELinux, YAMA and seccomp are Linux-only features.
>> 
>> I tested this patch locally, on my Fedora 30 machine (actually, a
>> Fedora Rawhide VM), but I'm not proposing a testcase for it because of
>> the difficulty of writing one.
>> 
>> WDYT?
>> 
>> gdb/doc/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 
>> 	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
>> 	section.
>> 
>> gdb/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 	    Jan Kratochvil  <jan.kratochvil@redhat.com>
>> 
>> 	* gdbsupport/gdb-dlfcn.c (gdb_dlopen): Add argument 'dont_throw'
>> 	Don't throw if it's true.
>> 	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Add optional argument
>> 	'dont_throw'.  Update comment.
>> 	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
>> 	function.
>> 	(inf_ptrace_me_fail_reason): New variable.
>> 	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
>> 	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
>> 	* linux-nat.c (linux_nat_target::attach): Update call to
>> 	'linux_ptrace_attach_fail_reason'.
>> 	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
>> 	* linux-nat.h (linux_nat_target) <ptrace_me_fail_reason>: New
>> 	method.
>> 	* nat/fork-inferior.c (trace_start_error_with_name): Add
>> 	optional 'append' argument.
>> 	* nat/fork-inferior.h (trace_start_error_with_name): Update
>> 	prototype.
>> 	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h" and
>> 	"nat/fork-inferior.h".
>> 	(selinux_ftype): New typedef.
>> 	(linux_ptrace_restricted_fail_reason): New function.
>> 	(linux_ptrace_attach_fail_reason): Add optional 'err'
>> 	argument.  Call 'linux_ptrace_restricted_fail_reason'.
>> 	(linux_ptrace_me_fail_reason): New function.
>> 	(linux_child_function): Handle ptrace error.
>> 	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
>> 	prototype.
>> 	(linux_ptrace_me_fail_reason): New function.
>> 	* target-delegates.c: Regenerate.
>> 	* target.h (struct target_ops) <ptrace_me_fail_reason>: New
>> 	method.
>> 	(target_ptrace_me_fail_reason): New define.
>> 
>> gdb/gdbserver/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 
>> 	* linux-low.c (linux_ptrace_fun): Call
>> 	'linux_ptrace_me_fail_reason'.
>> ---
>>  gdb/doc/gdb.texinfo        | 138 +++++++++++++++++++++++++++++++++++++
>>  gdb/gdbserver/linux-low.c  |   3 +-
>>  gdb/gdbsupport/gdb-dlfcn.c |   4 +-
>>  gdb/gdbsupport/gdb-dlfcn.h |   7 +-
>>  gdb/inf-ptrace.c           |  19 ++++-
>>  gdb/inf-ptrace.h           |  10 +++
>>  gdb/linux-nat.c            |   7 +-
>>  gdb/nat/fork-inferior.c    |   4 +-
>>  gdb/nat/fork-inferior.h    |   7 +-
>>  gdb/nat/linux-ptrace.c     |  86 +++++++++++++++++++++--
>>  gdb/nat/linux-ptrace.h     |  15 +++-
>>  11 files changed, 282 insertions(+), 18 deletions(-)
>> 
>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index e1bc8143e6..43f749c6f1 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -182,6 +182,9 @@ software in general.  We will miss him.
>>                                  @value{GDBN}
>>  * Operating System Information:: Getting additional information from
>>                                   the operating system
>> +* Linux kernel @code{ptrace} restrictions:: Restrictions sometimes
>> +                                            imposed by the Linux
>> +                                            kernel on @code{ptrace}
>>  * Trace File Format::		GDB trace file format
>>  * Index Section Format::        .gdb_index section format
>>  * Man Pages::			Manual pages
>> @@ -44682,6 +44685,141 @@ should contain a comma-separated list of cores that this process
>>  is running on.  Target may provide additional columns,
>>  which @value{GDBN} currently ignores.
>>  
>> +@node Linux kernel ptrace restrictions
>> +@appendix Linux kernel @code{ptrace} restrictions
>> +@cindex linux kernel ptrace restrictions, attach
>> +
>> +The @code{ptrace} system call is used by @value{GDBN} on GNU/Linux to,
>> +among other things, attach to a new or existing inferior in order to
>> +start debugging it.  Due to security concerns, some distributions and
>> +vendors disable or severily restrict the ability to perform these
>
> typo: severily -> severely

Fixed.

>
>
>> +operations, which can make @value{GDBN} malfunction.  In this section,
>> +we will expand on how this malfunction can manifest itself, and how to
>> +modify the system's settings in order to be able to use @value{GDBN}
>> +properly.
>> +
>> +@menu
>> +* The GDB error message::               The error message displayed when the
>> +                                        system prevents @value{GDBN} from using
>> +                                        @code{ptrace}
>> +* SELinux's @code{deny_ptrace}::        SELinux and the @code{deny_ptrace} option
>> +* Yama's @code{ptrace_scope}::          Yama and the @code{ptrace_scope} setting
>> +* Docker and @code{seccomp}::           Docker and the @code{seccomp}
>> +                                        infrastructure
>> +@end menu
>> +
>> +@node The GDB error message
>> +@appendixsection The @value{GDBN} error message
>> +
>> +When the system prevents @value{GDBN} from using the @code{ptrace}
>> +system call, you will likely see a descriptive error message
>> +explaining what is wrong and how to attempt to fix the problem.  For
>> +example, when SELinux's @code{deny_ptrace} option is enabled, you can
>> +see:
>> +
>> +@smallexample
>> +$ gdb program
>> +...
>> +(@value{GDBP}) run
>> +Starting program: program
>> +warning: Could not trace the inferior process.
>> +Error:
>> +warning: ptrace: Permission denied
>> +The SELinux 'deny_ptrace' option is enabled and preventing GDB
>> +from using 'ptrace'.  Please, disable it by executing (as root):
>> +
>> +  setsebool deny_ptrace off
>> +
>> +During startup program exited with code 127.
>> +(@value{GDBP})
>> +@end smallexample
>> +
>> +Sometimes, it may not be possible to acquire the necessary data to
>> +determine the root cause of the failure.  In this case, you will see a
>> +generic error message pointing you to this section:
>> +
>> +@smallexample
>> +$ gdb program
>> +...
>> +Starting program: program
>> +warning: Could not trace the inferior process.
>> +Error:
>> +warning: ptrace: Permission denied
>> +There might be restrictions preventing ptrace from working.  Please see
>> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>> +for more details.
>> +During startup program exited with code 127.
>> +(@value{GDBP})
>> +@end smallexample
>> +
>> +@node SELinux's deny_ptrace
>> +@appendixsection SELinux's @code{deny_ptrace}
>> +@cindex SELinux
>> +@cindex deny_ptrace
>> +
>> +If you are using SELinux, you might want to check whether the
>> +@code{deny_ptrace} option is enabled by doing:
>> +
>> +@smallexample
>> +$ getsebool deny_ptrace
>> +deny_ptrace --> on
>> +@end smallexample
>> +
>> +If the option is enabled, you can disable it by doing, as root:
>> +
>> +@smallexample
>> +# setsebool deny_ptrace off
>> +@end smallexample
>> +
>> +The option will be disabled until the next reboot.  If you would like
>> +to disable it permanently, you can do (as root):
>> +
>> +@smallexample
>> +# setsebool -P deny_ptrace off
>> +@end smallexample
>> +
>> +@node Yama's ptrace_scope
>> +@appendixsection Yama's @code{ptrace_scope}
>> +@cindex yama, ptrace_scope
>> +
>> +If your system has Yama enabled, you might want to check whether the
>> +@code{ptrace_scope} setting is enabled by checking the value of
>> +@file{/proc/sys/kernel/yama/ptrace_scope}:
>> +
>> +@smallexample
>> +$ cat /proc/sys/kernel/yama/ptrace_scope
>> +0
>> +@end smallexample
>> +
>> +If you see anything other than @code{0}, @value{GDBN} can be affected
>> +by it.  You can temporarily disable the feature by doing, as root:
>> +
>> +@smallexample
>> +# sysctl kernel.yama.ptrace_scope=0
>> +kernel.yama.ptrace_scope = 0
>> +@end smallexample
>> +
>> +You can make this permanent by doing, as root:
>> +
>> +@smallexample
>> +# sysctl -w kernel.yama.ptrace_scope=0
>> +kernel.yama.ptrace_scope = 0
>> +@end smallexample
>> +
>> +@node Docker and seccomp
>> +@appendixsection Docker and @code{seccomp}
>> +@cindex docker, seccomp
>> +
>> +If you are using Docker (@uref{https://www.docker.com/}) containers,
>> +you will probably have to disable its @code{seccomp} protections in
>> +order to be able to use @value{GDBN}.  To do that, you can use the
>> +options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
>> +when invoking Docker:
>> +
>> +@smallexample
>> +$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
>> +@end smallexample
>> +
>>  @node Trace File Format
>>  @appendix Trace File Format
>>  @cindex trace file format
>> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
>> index 3113017ae6..1e0a5cbf54 100644
>> --- a/gdb/gdbserver/linux-low.c
>> +++ b/gdb/gdbserver/linux-low.c
>> @@ -971,7 +971,8 @@ linux_ptrace_fun ()
>>  {
>>    if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
>>  	      (PTRACE_TYPE_ARG4) 0) < 0)
>> -    trace_start_error_with_name ("ptrace");
>> +    trace_start_error_with_name ("ptrace",
>> +				 linux_ptrace_me_fail_reason (errno).c_str ());
>>  
>>    if (setpgid (0, 0) < 0)
>>      trace_start_error_with_name ("setpgid");
>> diff --git a/gdb/gdbsupport/gdb-dlfcn.c b/gdb/gdbsupport/gdb-dlfcn.c
>> index 921f10f3d8..9e5a992c17 100644
>> --- a/gdb/gdbsupport/gdb-dlfcn.c
>> +++ b/gdb/gdbsupport/gdb-dlfcn.c
>> @@ -58,7 +58,7 @@ is_dl_available (void)
>>  #else /* NO_SHARED_LIB */
>>  
>>  gdb_dlhandle_up
>> -gdb_dlopen (const char *filename)
>> +gdb_dlopen (const char *filename, bool dont_throw)
>>  {
>>    void *result;
>>  #ifdef HAVE_DLFCN_H
>> @@ -66,7 +66,7 @@ gdb_dlopen (const char *filename)
>>  #elif __MINGW32__
>>    result = (void *) LoadLibrary (filename);
>>  #endif
>> -  if (result != NULL)
>> +  if (dont_throw || result != NULL)
>>      return gdb_dlhandle_up (result);
>>  
>>  #ifdef HAVE_DLFCN_H
>> diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
>> index 6a39d38941..a8ddbc03da 100644
>> --- a/gdb/gdbsupport/gdb-dlfcn.h
>> +++ b/gdb/gdbsupport/gdb-dlfcn.h
>> @@ -32,10 +32,11 @@ struct dlclose_deleter
>>  typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
>>  
>>  /* Load the dynamic library file named FILENAME, and return a handle
>> -   for that dynamic library.  Return NULL if the loading fails for any
>> -   reason.  */
>> +   for that dynamic library.  If the loading fails, return NULL if
>> +   DONT_THROW is true, or throw an exception otherwise (default
>> +   behaviour).  */
>>  
>> -gdb_dlhandle_up gdb_dlopen (const char *filename);
>> +gdb_dlhandle_up gdb_dlopen (const char *filename, bool dont_throw = false);
>>  
>>  /* Return the address of the symbol named SYMBOL inside the shared
>>     library whose handle is HANDLE.  Return NULL when the symbol could
>> diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
>> index 4a8e732373..b6cfd803cf 100644
>> --- a/gdb/inf-ptrace.c
>> +++ b/gdb/inf-ptrace.c
>> @@ -94,6 +94,22 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
>>  #endif /* PT_GET_PROCESS_STATE */
>>  \f
>>  
>> +/* Default method for "inf_ptrace_me_fail_reason", which returns an
>> +   empty string.  */
>> +
>> +static std::string
>> +default_inf_ptrace_me_fail_reason (int err)
>> +{
>> +  return {};
>> +}
>> +
>> +/* Point to "inf_ptrace_me_fail_reason", 
>
> Did you mean "Pointer"?  This reads strange because 
> "inf_ptrace_me_fail_reason" is the name of the pointer itself,
> so how to point _to_ it?

Yes, I meant "Pointer", thanks.  Fixed.

> which implements a function
>> +   that can be called by "inf_ptrace_me" in order to obtain the reason
>> +   for failure.  */
>> +
>> +std::string (*inf_ptrace_me_fail_reason) (int err)
>> +  = default_inf_ptrace_me_fail_reason;
>> +
>>  /* Prepare to be traced.  */
>>  
>>  static void
>> @@ -101,7 +117,8 @@ inf_ptrace_me (void)
>>  {
>>    /* "Trace me, Dr. Memory!"  */
>>    if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
>> -    trace_start_error_with_name ("ptrace");
>> +    trace_start_error_with_name ("ptrace",
>> +				 inf_ptrace_me_fail_reason (errno).c_str ());
>>  }
>>  
>>  /* Start a new inferior Unix child process.  EXEC_FILE is the file to
>> diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
>> index 98b5d2e09e..7cdab9af89 100644
>> --- a/gdb/inf-ptrace.h
>> +++ b/gdb/inf-ptrace.h
>> @@ -83,4 +83,14 @@ protected:
>>  
>>  extern pid_t get_ptrace_pid (ptid_t);
>>  
>> +/* Pointer to "inf_ptrace_me_fail_reason", which implements a function
>
> Ah, here it's "pointer".  But still reads strange.  I think the comment
> in the .c file should just say the usual "See inf-ptrace.h.".

Indeed.  I'll update the comment there.

>
>> +   that can be called by "inf_ptrace_me" in order to obtain the reason
>> +   for a ptrace failure.  ERR is the ERRNO value set by the failing
>> +   ptrace call.
>> +
>> +   This pointer can be overriden by targets that want to personalize
>> +   the error message printed when ptrace fails (see linux-nat.c, for
>> +   example).  */
>> +extern std::string (*inf_ptrace_me_fail_reason) (int err);
>> +
>>  #endif
>> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
>> index 945c19f666..b5a9eaf72e 100644
>> --- a/gdb/linux-nat.c
>> +++ b/gdb/linux-nat.c
>> @@ -1191,8 +1191,9 @@ linux_nat_target::attach (const char *args, int from_tty)
>>      }
>>    catch (const gdb_exception_error &ex)
>>      {
>> +      int saved_errno = errno;
>>        pid_t pid = parse_pid_to_attach (args);
>> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
>> +      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>>  
>>        if (!reason.empty ())
>>  	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
>> @@ -4696,6 +4697,10 @@ Enables printf debugging output."),
>>    sigemptyset (&blocked_mask);
>>  
>>    lwp_lwpid_htab_create ();
>> +
>> +  /* Set the proper function to generate a message when ptrace
>> +     fails.  */
>> +  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
>>  }
>>  \f
>>  
>> diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
>> index 68b51aa814..72ac623e20 100644
>> --- a/gdb/nat/fork-inferior.c
>> +++ b/gdb/nat/fork-inferior.c
>> @@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
>>  /* See nat/fork-inferior.h.  */
>>  
>>  void
>> -trace_start_error_with_name (const char *string)
>> +trace_start_error_with_name (const char *string, const char *append)
>>  {
>> -  trace_start_error ("%s: %s", string, safe_strerror (errno));
>> +  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
>>  }
>> diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
>> index 1d0519fb26..7e6b889210 100644
>> --- a/gdb/nat/fork-inferior.h
>> +++ b/gdb/nat/fork-inferior.h
>> @@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
>>    ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
>>  
>>  /* Like "trace_start_error", but the error message is constructed by
>> -   combining STRING with the system error message for errno.  This
>> -   function does not return.  */
>> -extern void trace_start_error_with_name (const char *string)
>> +   combining STRING with the system error message for errno, and
>> +   (optionally) with APPEND.  This function does not return.  */
>> +extern void trace_start_error_with_name (const char *string,
>> +					 const char *append = "")
>>    ATTRIBUTE_NORETURN;
>>  
>>  #endif /* NAT_FORK_INFERIOR_H */
>> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
>> index c1ebc0a032..599d9cfb55 100644
>> --- a/gdb/nat/linux-ptrace.c
>> +++ b/gdb/nat/linux-ptrace.c
>> @@ -21,6 +21,8 @@
>>  #include "linux-procfs.h"
>>  #include "linux-waitpid.h"
>>  #include "gdbsupport/buffer.h"
>> +#include "gdbsupport/gdb-dlfcn.h"
>> +#include "nat/fork-inferior.h"
>>  #ifdef HAVE_SYS_PROCFS_H
>>  #include <sys/procfs.h>
>>  #endif
>> @@ -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);
>> +
>> +  if (handle.get () != NULL)
>
> No need for .get() here.  This:
>
>   if (handle != NULL)
>
> works the same.

While writing this code I thought I remembered something like that, but
I wasn't sure.  Thanks for clarifying.  Updated.

> (I'd write nullptr throughout instead of NULL in new code.)

OK.  Updated.

>> +    {
>> +      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");
>> +
>
> Use gdb_fopen_cloexec ?

Ah, I hadn't realized that this existed.  Much better, thanks!

>> +  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);
>
> If ERR is an errno number, then it's a bit odd to use -1 for default,
> since errno == 0 is the traditional "no error" number.  Pedantically, I believe
> there's no garantee that a valid error number must be a positive integer.
>
> But, why the default argument in the first place?  What calls this
> without passing an error?

So, the only place that calls linux_ptrace_attach_fail_reason without
passing the ERR argument is linux_ptrace_attach_fail_reason_string:

  std::string
  linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
  {
    long lwpid = ptid.lwp ();
    std::string reason = linux_ptrace_attach_fail_reason (lwpid);

    if (!reason.empty ())
      return string_printf ("%s (%d), %s", safe_strerror (err), err,
                            reason.c_str ());
    else
      return string_printf ("%s (%d)", safe_strerror (err), err);
  }

In this case, I opted to keep it as is because the function will compose
a string contaning like:

  A (B)[: C]

Where:

 A = safe_strerror
 B = errno
 C = fail reason (optional)

This function (linux_ptrace_attach_fail_reason_string) is called in
three places:

gdb/linux-nat.c:

	      std::string reason
		= linux_ptrace_attach_fail_reason_string (ptid, err);

	      warning (_("Cannot attach to lwp %d: %s"),
		       lwpid, reason.c_str ());

gdb/gdbserver/linux-low.c:

	  std::string reason
	    = linux_ptrace_attach_fail_reason_string (ptid, err);

	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());

and

      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());


It seems to me like these error messages are expecting a short string to
just append to their existing strings, so I didn't think it made much
sense to extend the ptrace error checking here as well.  That's why I
didn't extend linux_ptrace_attach_fail_reason_string to pass ERR down to
linux_ptrace_attach_fail_reason.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-29 19:27     ` Sergio Durigan Junior
@ 2019-08-29 19:48       ` Sergio Durigan Junior
  2019-08-30 19:03         ` Pedro Alves
  2019-08-30 19:51         ` [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message Sergio Durigan Junior
  2019-08-30 12:45       ` [PATCH v2] Improve ptrace-error detection on Linux targets Pedro Alves
  1 sibling, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-29 19:48 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, August 29 2019, I wrote:

> On Thursday, August 29 2019, Pedro Alves wrote:
>
>>>   - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
>>>     checks if it's different than 0.
>>> 
>>> For each of these checks, if it succeeds, the user will see a message
>>> informing about the restriction in place, and how it can be disabled.
>>> For example, if "deny_ptrace" is enabled, the user will see:
>>> 
>>>   # gdb /bin/true
>>>   ...
>>>   Starting program: /usr/bin/true
>>>   warning: Could not trace the inferior process.
>>>   Error:
>>>   warning: ptrace: Permission denied
>>
>> Curious, that:
>>
>>  warning:
>>  Error:
>>  warning:
>>
>> looks a bit odd, specifically the "Error:" line.  Do you know where is
>> that coming from?
>
> Not offhand; I'll investigate and come back with the results.

So, the problem is with nat/fork-inferior.c:trace_start_error:

  void
  trace_start_error (const char *fmt, ...)
  {
    va_list ap;

    va_start (ap, fmt);
    warning ("Could not trace the inferior process.\nError: ");
    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    vwarning (fmt, ap);
    va_end (ap);

    gdb_flush_out_err ();
    _exit (0177);
  }

  /* See nat/fork-inferior.h.  */

  void
  trace_start_error_with_name (const char *string, const char *append)
  {
    trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
  }


You can see that we're calling "warning", which puts a newline at the
end of the string.  I think it'd be best to just get rid of the
"\nError: " suffix.  We'd then have something like:

   Starting program: /usr/bin/true
   warning: Could not trace the inferior process.
   warning: ptrace: Permission denied
   ....

WDYT?

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-29 19:27     ` Sergio Durigan Junior
  2019-08-29 19:48       ` Sergio Durigan Junior
@ 2019-08-30 12:45       ` Pedro Alves
  2019-09-04 19:21         ` Sergio Durigan Junior
  2019-09-04 19:31         ` Sergio Durigan Junior
  1 sibling, 2 replies; 98+ messages in thread
From: Pedro Alves @ 2019-08-30 12:45 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 8/29/19 8:27 PM, Sergio Durigan Junior wrote:
> On Thursday, August 29 2019, Pedro Alves wrote:

>>> 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);
>>
>> If ERR is an errno number, then it's a bit odd to use -1 for default,
>> since errno == 0 is the traditional "no error" number.  Pedantically, I believe
>> there's no garantee that a valid error number must be a positive integer.
>>
>> But, why the default argument in the first place?  What calls this
>> without passing an error?
> 
> So, the only place that calls linux_ptrace_attach_fail_reason without
> passing the ERR argument is linux_ptrace_attach_fail_reason_string:
> 
>   std::string
>   linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
>   {
>     long lwpid = ptid.lwp ();
>     std::string reason = linux_ptrace_attach_fail_reason (lwpid);
> 
>     if (!reason.empty ())
>       return string_printf ("%s (%d), %s", safe_strerror (err), err,
>                             reason.c_str ());
>     else
>       return string_printf ("%s (%d)", safe_strerror (err), err);
>   }
> 
> In this case, I opted to keep it as is because the function will compose
> a string contaning like:
> 
>   A (B)[: C]
> 
> Where:
> 
>  A = safe_strerror
>  B = errno
>  C = fail reason (optional)
> 
> This function (linux_ptrace_attach_fail_reason_string) is called in
> three places:
> 
> gdb/linux-nat.c:
> 
> 	      std::string reason
> 		= linux_ptrace_attach_fail_reason_string (ptid, err);
> 
> 	      warning (_("Cannot attach to lwp %d: %s"),
> 		       lwpid, reason.c_str ());
> 
> gdb/gdbserver/linux-low.c:
> 
> 	  std::string reason
> 	    = linux_ptrace_attach_fail_reason_string (ptid, err);
> 
> 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
> 
> and
> 
>       std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
>       error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
> 
> 
> It seems to me like these error messages are expecting a short string to
> just append to their existing strings, so I didn't think it made much
> sense to extend the ptrace error checking here as well.  That's why I
> didn't extend linux_ptrace_attach_fail_reason_string to pass ERR down to
> linux_ptrace_attach_fail_reason.

OK, I think that's just a bit too messy.  Let's take a fresh look at the
result:

 /* 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);

Both the functions have the exact same prototype (same parameters, same return).
And since they both return std::string, why is linux_ptrace_attach_fail_reason_string
called "xxx_string"?  From the function names alone, I'd think
linux_ptrace_attach_fail_reason_string returned a string, while
linux_ptrace_attach_fail_reason returned something else, or printed
directly.  But that's not the case.

So linux_ptrace_attach_fail_reason_string is used when we're attaching
to an lwp, other than the thread group leader (the main thread).
IMO, it'd be better to rename that function accordingly, and update
its comments accordingly too.

Then, it becomes clearer that linux_ptrace_attach_fail_reason is to
be used in the initial process attach, in which case we want to
check the ptrace permissions.

Also, we can factor out things such that we don't need the
default parameter.  I tend to prefer avoiding mode-setting default
parameters, preferring differently named functions, because default
parameters make it harder to grep around the sources, distinguishing the
cases where you passed an argument or not.  In this case,
the linux_ptrace_attach_fail_reason / linux_ptrace_attach_fail_reason_string
functions serve different purposes, so decoupling them via not using
default parameters is better, IMO, it avoids missing some conversion
in some spot.

Which is exactly what happened.  Here's a patch implementing
the idea.  Notice how gdbserver/linux-low.c:linux_attach
has a spot where it is currently using linux_ptrace_attach_fail_reason_string
after attaching to the main thread fails.  That spot should be including
the new ptrace restrictions fail reasons, but it wasn't due to use of
linux_ptrace_attach_fail_reason_string:

 @@ -1202,7 +1202,7 @@ linux_attach (unsigned long pid)
     {
       remove_process (proc);
 
 -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
 +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
       error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
     }

I haven't checked whether the resulting error message looks good as is,
or whether we need to tweak that error call in addition.  Can you take it
from here?

Here's the patch on top of yours.

From a3e8744c1ed7fa8c702009fe3caf8578bb1785ea Mon Sep 17 00:00:00 2001
From: Pedro Alves <palves@redhat.com>
Date: Fri, 30 Aug 2019 13:15:12 +0100
Subject: [PATCH] linux_ptrace_attach_fail_reason

---
 gdb/gdbserver/linux-low.c |  4 ++--
 gdb/gdbserver/thread-db.c |  2 +-
 gdb/linux-nat.c           |  2 +-
 gdb/nat/linux-ptrace.c    | 24 ++++++++++++++++++------
 gdb/nat/linux-ptrace.h    | 10 ++++++----
 5 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 1e0a5cbf54d..45cf43ad9ed 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -1170,7 +1170,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1202,7 +1202,7 @@ linux_attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
       error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
     }
 
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index b2791b0513a..cfba05977e6 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -225,7 +225,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index b5a9eaf72e3..22cb55e6dcc 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1136,7 +1136,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 599d9cfb550..2201a24d812 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -92,10 +92,13 @@ for more details.\n");
   return ret;
 }
 
-/* See declaration in linux-ptrace.h.  */
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid, int err)
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -111,8 +114,17 @@ linux_ptrace_attach_fail_reason (pid_t pid, int err)
 		      "terminated"),
 		    (int) pid);
 
-  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+  return result;
+}
 
+/* See declaration in linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
   if (!ptrace_restrict.empty ())
     result += "\n" + ptrace_restrict;
 
@@ -122,10 +134,10 @@ linux_ptrace_attach_fail_reason (pid_t pid, int err)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 04ada53bf69..94c9ba48ba5 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -180,12 +180,14 @@ struct buffer;
    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);
+extern std::string linux_ptrace_attach_fail_reason (pid_t pid, int err);
 
-/* Find all possible reasons we could have failed to attach to PTID
+/* Find all possible reasons we could have failed to attach to LWPID
    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);
+   with (an errno).  Unlike linux_ptrace_attach_fail_reason, this
+   function should be used when attaching to an LWP other than the
+   leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t lwpid, 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
-- 
2.14.5

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
                     ` (2 preceding siblings ...)
  2019-08-29 14:40   ` Pedro Alves
@ 2019-08-30 12:47   ` Pedro Alves
  2019-08-30 14:07     ` Eli Zaretskii
  3 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-08-30 12:47 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches; +Cc: Eli Zaretskii, Ruslan Kabatsayev

FYI, I'm seeing these warnings with your patch:

$ make
makeinfo --split-size=5000000  -DHAVE_MAKEINFO_CLICK -I /home/pedro/gdb/binutils-gdb/src/gdb/doc/../../readline/doc -I /home/pedro/gdb/binutils-gdb/src/gdb/doc/../mi -I /home/pedro/gdb/binutils-gdb/src/gdb/doc \
        -o gdb.info /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo
/home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:185: warning: @menu entry node name `Linux kernel @code{ptrace} restrictions' different from node name `Linux kernel ptrace restrictions'
/home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44712: warning: @menu entry node name `SELinux's @code{deny_ptrace}' different from node name `SELinux's deny_ptrace'
/home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44713: warning: @menu entry node name `Yama's @code{ptrace_scope}' different from node name `Yama's ptrace_scope'
/home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44714: warning: @menu entry node name `Docker and @code{seccomp}' different from node name `Docker and seccomp'

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-30 12:47   ` Pedro Alves
@ 2019-08-30 14:07     ` Eli Zaretskii
  2019-08-30 19:44       ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Eli Zaretskii @ 2019-08-30 14:07 UTC (permalink / raw)
  To: Pedro Alves; +Cc: sergiodj, gdb-patches, b7.10110111

> Cc: Eli Zaretskii <eliz@gnu.org>, Ruslan Kabatsayev <b7.10110111@gmail.com>
> From: Pedro Alves <palves@redhat.com>
> Date: Fri, 30 Aug 2019 13:47:06 +0100
> 
> FYI, I'm seeing these warnings with your patch:
> 
> $ make
> makeinfo --split-size=5000000  -DHAVE_MAKEINFO_CLICK -I /home/pedro/gdb/binutils-gdb/src/gdb/doc/../../readline/doc -I /home/pedro/gdb/binutils-gdb/src/gdb/doc/../mi -I /home/pedro/gdb/binutils-gdb/src/gdb/doc \
>         -o gdb.info /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo
> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:185: warning: @menu entry node name `Linux kernel @code{ptrace} restrictions' different from node name `Linux kernel ptrace restrictions'
> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44712: warning: @menu entry node name `SELinux's @code{deny_ptrace}' different from node name `SELinux's deny_ptrace'
> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44713: warning: @menu entry node name `Yama's @code{ptrace_scope}' different from node name `Yama's ptrace_scope'
> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44714: warning: @menu entry node name `Docker and @code{seccomp}' different from node name `Docker and seccomp'

We shouldn't use @-commands in node names.

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-29 19:48       ` Sergio Durigan Junior
@ 2019-08-30 19:03         ` Pedro Alves
  2019-08-30 19:51         ` [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message Sergio Durigan Junior
  1 sibling, 0 replies; 98+ messages in thread
From: Pedro Alves @ 2019-08-30 19:03 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 8/29/19 8:48 PM, Sergio Durigan Junior wrote:

> You can see that we're calling "warning", which puts a newline at the
> end of the string.  I think it'd be best to just get rid of the
> "\nError: " suffix.  We'd then have something like:
> 
>    Starting program: /usr/bin/true
>    warning: Could not trace the inferior process.
>    warning: ptrace: Permission denied
>    ....
> 
> WDYT?

Sounds fine to me.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-30 14:07     ` Eli Zaretskii
@ 2019-08-30 19:44       ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-30 19:44 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: Pedro Alves, gdb-patches, b7.10110111

On Friday, August 30 2019, Eli Zaretskii wrote:

>> Cc: Eli Zaretskii <eliz@gnu.org>, Ruslan Kabatsayev <b7.10110111@gmail.com>
>> From: Pedro Alves <palves@redhat.com>
>> Date: Fri, 30 Aug 2019 13:47:06 +0100
>> 
>> FYI, I'm seeing these warnings with your patch:
>> 
>> $ make
>> makeinfo --split-size=5000000 -DHAVE_MAKEINFO_CLICK -I
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/../../readline/doc -I
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/../mi -I
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc \
>>         -o gdb.info /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:185: warning:
>> @menu entry node name `Linux kernel @code{ptrace} restrictions'
>> different from node name `Linux kernel ptrace restrictions'
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44712: warning: @menu entry node name `SELinux's @code{deny_ptrace}' different from node name `SELinux's deny_ptrace'
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44713: warning: @menu entry node name `Yama's @code{ptrace_scope}' different from node name `Yama's ptrace_scope'
>> /home/pedro/gdb/binutils-gdb/src/gdb/doc/gdb.texinfo:44714: warning: @menu entry node name `Docker and @code{seccomp}' different from node name `Docker and seccomp'
>
> We shouldn't use @-commands in node names.

But I fixed my patch, and v2 doesn't use @-commands in node names
anymore.

Hm, I think the problem is that I'm using @-commands inside @menu.  OK,
I'll fix that, and I'll also remove @-commands from @appendix.  Thanks
for pointing it out.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message
  2019-08-29 19:48       ` Sergio Durigan Junior
  2019-08-30 19:03         ` Pedro Alves
@ 2019-08-30 19:51         ` Sergio Durigan Junior
  2019-08-30 19:54           ` Pedro Alves
  1 sibling, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-30 19:51 UTC (permalink / raw)
  To: GDB Patches; +Cc: Pedro Alves, Sergio Durigan Junior

Rationale: https://sourceware.org/ml/gdb-patches/2019-08/msg00651.html

This very simple patch removes the "\nError: " suffix from the warning
message printed by nat/fork-inferior.c:trace_start_error.  This proved
to just pollute the screen, causing things like:

  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  Error:
  warning: ptrace: Permission denied

This "Error: " string is not useful at all, and can confuse things,
therefore let's just remove it and simplify the resulting messages:

  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Permission denied

2019-08-29  Sergio Durigan Junior  <sergiodj@redhat.com>

	* nat/fork-inferior.c (trace_start_error): Remove "\nError: "
	suffix from warning message.
---
 gdb/nat/fork-inferior.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 68b51aa814..355e9bef43 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -580,7 +580,7 @@ trace_start_error (const char *fmt, ...)
   va_list ap;
 
   va_start (ap, fmt);
-  warning ("Could not trace the inferior process.\nError: ");
+  warning ("Could not trace the inferior process.");
   vwarning (fmt, ap);
   va_end (ap);
 
-- 
2.21.0

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message
  2019-08-30 19:51         ` [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message Sergio Durigan Junior
@ 2019-08-30 19:54           ` Pedro Alves
  2019-08-30 21:06             ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-08-30 19:54 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches

On 8/30/19 8:51 PM, Sergio Durigan Junior wrote:
> Rationale: https://sourceware.org/ml/gdb-patches/2019-08/msg00651.html
> 
> This very simple patch removes the "\nError: " suffix from the warning
> message printed by nat/fork-inferior.c:trace_start_error.  This proved
> to just pollute the screen, causing things like:
> 
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   Error:
>   warning: ptrace: Permission denied
> 
> This "Error: " string is not useful at all, and can confuse things,
> therefore let's just remove it and simplify the resulting messages:
> 
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   warning: ptrace: Permission denied
> 
> 2019-08-29  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* nat/fork-inferior.c (trace_start_error): Remove "\nError: "
> 	suffix from warning message.

OK.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message
  2019-08-30 19:54           ` Pedro Alves
@ 2019-08-30 21:06             ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-08-30 21:06 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches

On Friday, August 30 2019, Pedro Alves wrote:

> On 8/30/19 8:51 PM, Sergio Durigan Junior wrote:
>> Rationale: https://sourceware.org/ml/gdb-patches/2019-08/msg00651.html
>> 
>> This very simple patch removes the "\nError: " suffix from the warning
>> message printed by nat/fork-inferior.c:trace_start_error.  This proved
>> to just pollute the screen, causing things like:
>> 
>>   Starting program: /usr/bin/true
>>   warning: Could not trace the inferior process.
>>   Error:
>>   warning: ptrace: Permission denied
>> 
>> This "Error: " string is not useful at all, and can confuse things,
>> therefore let's just remove it and simplify the resulting messages:
>> 
>>   Starting program: /usr/bin/true
>>   warning: Could not trace the inferior process.
>>   warning: ptrace: Permission denied
>> 
>> 2019-08-29  Sergio Durigan Junior  <sergiodj@redhat.com>
>> 
>> 	* nat/fork-inferior.c (trace_start_error): Remove "\nError: "
>> 	suffix from warning message.
>
> OK.

Thanks, pushed: 47a536d940d2f2bccfec51539b857da06ebc429e

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-30 12:45       ` [PATCH v2] Improve ptrace-error detection on Linux targets Pedro Alves
@ 2019-09-04 19:21         ` Sergio Durigan Junior
  2019-09-04 19:31         ` Sergio Durigan Junior
  1 sibling, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 19:21 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Friday, August 30 2019, Pedro Alves wrote:

> On 8/29/19 8:27 PM, Sergio Durigan Junior wrote:
>> On Thursday, August 29 2019, Pedro Alves wrote:
>
>>>> 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);
>>>
>>> If ERR is an errno number, then it's a bit odd to use -1 for default,
>>> since errno == 0 is the traditional "no error" number.  Pedantically, I believe
>>> there's no garantee that a valid error number must be a positive integer.
>>>
>>> But, why the default argument in the first place?  What calls this
>>> without passing an error?
>> 
>> So, the only place that calls linux_ptrace_attach_fail_reason without
>> passing the ERR argument is linux_ptrace_attach_fail_reason_string:
>> 
>>   std::string
>>   linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
>>   {
>>     long lwpid = ptid.lwp ();
>>     std::string reason = linux_ptrace_attach_fail_reason (lwpid);
>> 
>>     if (!reason.empty ())
>>       return string_printf ("%s (%d), %s", safe_strerror (err), err,
>>                             reason.c_str ());
>>     else
>>       return string_printf ("%s (%d)", safe_strerror (err), err);
>>   }
>> 
>> In this case, I opted to keep it as is because the function will compose
>> a string contaning like:
>> 
>>   A (B)[: C]
>> 
>> Where:
>> 
>>  A = safe_strerror
>>  B = errno
>>  C = fail reason (optional)
>> 
>> This function (linux_ptrace_attach_fail_reason_string) is called in
>> three places:
>> 
>> gdb/linux-nat.c:
>> 
>> 	      std::string reason
>> 		= linux_ptrace_attach_fail_reason_string (ptid, err);
>> 
>> 	      warning (_("Cannot attach to lwp %d: %s"),
>> 		       lwpid, reason.c_str ());
>> 
>> gdb/gdbserver/linux-low.c:
>> 
>> 	  std::string reason
>> 	    = linux_ptrace_attach_fail_reason_string (ptid, err);
>> 
>> 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
>> 
>> and
>> 
>>       std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
>>       error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
>> 
>> 
>> It seems to me like these error messages are expecting a short string to
>> just append to their existing strings, so I didn't think it made much
>> sense to extend the ptrace error checking here as well.  That's why I
>> didn't extend linux_ptrace_attach_fail_reason_string to pass ERR down to
>> linux_ptrace_attach_fail_reason.
>
> OK, I think that's just a bit too messy.  Let's take a fresh look at the
> result:
>
>  /* 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);
>
> Both the functions have the exact same prototype (same parameters, same return).
> And since they both return std::string, why is linux_ptrace_attach_fail_reason_string
> called "xxx_string"?  From the function names alone, I'd think
> linux_ptrace_attach_fail_reason_string returned a string, while
> linux_ptrace_attach_fail_reason returned something else, or printed
> directly.  But that's not the case.
>
> So linux_ptrace_attach_fail_reason_string is used when we're attaching
> to an lwp, other than the thread group leader (the main thread).
> IMO, it'd be better to rename that function accordingly, and update
> its comments accordingly too.
>
> Then, it becomes clearer that linux_ptrace_attach_fail_reason is to
> be used in the initial process attach, in which case we want to
> check the ptrace permissions.
>
> Also, we can factor out things such that we don't need the
> default parameter.  I tend to prefer avoiding mode-setting default
> parameters, preferring differently named functions, because default
> parameters make it harder to grep around the sources, distinguishing the
> cases where you passed an argument or not.  In this case,
> the linux_ptrace_attach_fail_reason / linux_ptrace_attach_fail_reason_string
> functions serve different purposes, so decoupling them via not using
> default parameters is better, IMO, it avoids missing some conversion
> in some spot.
>
> Which is exactly what happened.  Here's a patch implementing
> the idea.  Notice how gdbserver/linux-low.c:linux_attach
> has a spot where it is currently using linux_ptrace_attach_fail_reason_string
> after attaching to the main thread fails.  That spot should be including
> the new ptrace restrictions fail reasons, but it wasn't due to use of
> linux_ptrace_attach_fail_reason_string:
>
>  @@ -1202,7 +1202,7 @@ linux_attach (unsigned long pid)
>      {
>        remove_process (proc);
>  
>  -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
>  +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
>        error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
>      }
>
> I haven't checked whether the resulting error message looks good as is,
> or whether we need to tweak that error call in addition.  Can you take it
> from here?
>
> Here's the patch on top of yours.

Sure, thanks for the patch.  I've tried it here and it seems fine.  I'll
submit it integrated into v3 soon.

>
> From a3e8744c1ed7fa8c702009fe3caf8578bb1785ea Mon Sep 17 00:00:00 2001
> From: Pedro Alves <palves@redhat.com>
> Date: Fri, 30 Aug 2019 13:15:12 +0100
> Subject: [PATCH] linux_ptrace_attach_fail_reason
>
> ---
>  gdb/gdbserver/linux-low.c |  4 ++--
>  gdb/gdbserver/thread-db.c |  2 +-
>  gdb/linux-nat.c           |  2 +-
>  gdb/nat/linux-ptrace.c    | 24 ++++++++++++++++++------
>  gdb/nat/linux-ptrace.h    | 10 ++++++----
>  5 files changed, 28 insertions(+), 14 deletions(-)
>
> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index 1e0a5cbf54d..45cf43ad9ed 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -1170,7 +1170,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>        else if (err != 0)
>  	{
>  	  std::string reason
> -	    = linux_ptrace_attach_fail_reason_string (ptid, err);
> +	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>  	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
>  	}
> @@ -1202,7 +1202,7 @@ linux_attach (unsigned long pid)
>      {
>        remove_process (proc);
>  
> -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
>        error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
>      }
>  
> diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
> index b2791b0513a..cfba05977e6 100644
> --- a/gdb/gdbserver/thread-db.c
> +++ b/gdb/gdbserver/thread-db.c
> @@ -225,7 +225,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
>    err = linux_attach_lwp (ptid);
>    if (err != 0)
>      {
> -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
> +      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>        warning ("Could not attach to thread %ld (LWP %d): %s",
>  	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index b5a9eaf72e3..22cb55e6dcc 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -1136,7 +1136,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>  	  else
>  	    {
>  	      std::string reason
> -		= linux_ptrace_attach_fail_reason_string (ptid, err);
> +		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>  	      warning (_("Cannot attach to lwp %d: %s"),
>  		       lwpid, reason.c_str ());
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index 599d9cfb550..2201a24d812 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -92,10 +92,13 @@ for more details.\n");
>    return ret;
>  }
>  
> -/* See declaration in linux-ptrace.h.  */
> +/* 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.  Helper for linux_ptrace_attach_fail_reason and
> +   linux_ptrace_attach_fail_reason_lwp.  */
>  
> -std::string
> -linux_ptrace_attach_fail_reason (pid_t pid, int err)
> +static std::string
> +linux_ptrace_attach_fail_reason_1 (pid_t pid)
>  {
>    pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
>    std::string result;
> @@ -111,8 +114,17 @@ linux_ptrace_attach_fail_reason (pid_t pid, int err)
>  		      "terminated"),
>  		    (int) pid);
>  
> -  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
> +  return result;
> +}
>  
> +/* See declaration in linux-ptrace.h.  */
> +
> +std::string
> +linux_ptrace_attach_fail_reason (pid_t pid, int err)
> +{
> +  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
> +
> +  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
>    if (!ptrace_restrict.empty ())
>      result += "\n" + ptrace_restrict;
>  
> @@ -122,10 +134,10 @@ linux_ptrace_attach_fail_reason (pid_t pid, int err)
>  /* See linux-ptrace.h.  */
>  
>  std::string
> -linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
> +linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
>  {
>    long lwpid = ptid.lwp ();
> -  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
> +  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
>  
>    if (!reason.empty ())
>      return string_printf ("%s (%d), %s", safe_strerror (err), err,
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index 04ada53bf69..94c9ba48ba5 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -180,12 +180,14 @@ struct buffer;
>     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);
> +extern std::string linux_ptrace_attach_fail_reason (pid_t pid, int err);
>  
> -/* Find all possible reasons we could have failed to attach to PTID
> +/* Find all possible reasons we could have failed to attach to LWPID
>     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);
> +   with (an errno).  Unlike linux_ptrace_attach_fail_reason, this
> +   function should be used when attaching to an LWP other than the
> +   leader; it does not warn about ptrace restrictions.  */
> +extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t lwpid, 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
> -- 
> 2.14.5

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-08-30 12:45       ` [PATCH v2] Improve ptrace-error detection on Linux targets Pedro Alves
  2019-09-04 19:21         ` Sergio Durigan Junior
@ 2019-09-04 19:31         ` Sergio Durigan Junior
  2019-09-04 19:58           ` Pedro Alves
  1 sibling, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 19:31 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Friday, August 30 2019, Pedro Alves wrote:

> Which is exactly what happened.  Here's a patch implementing
> the idea.  Notice how gdbserver/linux-low.c:linux_attach
> has a spot where it is currently using linux_ptrace_attach_fail_reason_string
> after attaching to the main thread fails.  That spot should be including
> the new ptrace restrictions fail reasons, but it wasn't due to use of
> linux_ptrace_attach_fail_reason_string:
>
>  @@ -1202,7 +1202,7 @@ linux_attach (unsigned long pid)
>      {
>        remove_process (proc);
>  
>  -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
>  +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
>        error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
>      }
>
> I haven't checked whether the resulting error message looks good as is,
> or whether we need to tweak that error call in addition.  Can you take it
> from here?

I forgot to say, but the way my patch works now prevents the code path
above to be executed.  My patch catches the ptrace failure very early in
gdbserver initialization the process, when linux_child_function is
called during the execution of linux_check_ptrace_features.  Since
linux_child_function will try to perform a PTRACE_TRACEME (and fail if
there are any restrictions in place), gdbserver will error out before it
even tries to spawn/attach.

Nevertheless, during my tests I bypassed linux_check_ptrace_features and
confirmed that the error message from linux_attach (above) is correctly
displayed:

  [sergio@fedora-rawhide build]$ sleep 120 &
  [5] 2732388
  [sergio@fedora-rawhide build]$ ./gdb/gdbserver/gdbserver :9911 --attach 2732388
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2732391 No such process
  Cannot attach to process 2732388: 

  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  Exiting

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v3] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
                   ` (3 preceding siblings ...)
  2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
@ 2019-09-04 19:54 ` Sergio Durigan Junior
  2019-09-05 17:04   ` Eli Zaretskii
  2019-09-11  1:11 ` [PATCH v4] " Sergio Durigan Junior
  2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
  6 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 19:54 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev, Sergio Durigan Junior

Changes since v2:

- Fixed nits pointed by Pedro.  Incorporated his patch to improve the
  linux_ptrace_attach_fail_reason_* funtions.

- Fixed documentation issues.



In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_ME will fail
with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

My first thought (and attempt) was to make GDB print a generic warning
when a ptrace error happened; this message would just point the user
to our documentation, where she could find more information about
possible causes for the error (and try to diagnose/fix the problem).
This proved to be too simple, and I was convinced that it is actually
a good idea to go the extra kilometre and try to pinpoint the specific
problem (or problems) preventing ptrace from working, as well as
provide useful suggestions on how the user can fix things.

Here is the patch I came up with.  It implements a new function,
'linux_ptrace_restricted_fail_reason', which does a few things to
check what's wrong with ptrace:

  - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
    option is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  Error:
  warning: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  During startup program exited with code 127.

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  Error:
  warning: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  During startup program exited with code 127.

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  gdbserver: Could not trace the inferior process.
  Error:
  gdbserver: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  linux_check_ptrace_features: waitpid: unexpected status 32512.
  Exiting

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.

This means that the patch expands our documentation and creates a new
appendix section named "Linux kernel ptrace restrictions", with
sub-sections for each possible restriction that might be in place.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

I tested this patch locally, on my Fedora 30 machine (actually, a
Fedora Rawhide VM), but I'm not proposing a testcase for it because of
the difficulty of writing one.

WDYT?

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* gdbsupport/gdb-dlfcn.c (gdb_dlopen): Add argument 'dont_throw'
	Don't throw if it's true.
	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Add optional argument
	'dont_throw'.  Update comment.
	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
	function.
	(inf_ptrace_me_fail_reason): New variable.
	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
	* linux-nat.c (attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_nat_target::attach): Update call to
	'linux_ptrace_attach_fail_reason'.
	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
	* nat/fork-inferior.c (trace_start_error_with_name): Add
	optional 'append' argument.
	* nat/fork-inferior.h (trace_start_error_with_name): Update
	prototype.
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
	"gdbsupport/filestuff.h" and "nat/fork-inferior.h".
	(selinux_ftype): New typedef.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_1): New function.
	(linux_ptrace_attach_fail_reason): Change first argument type
	from 'ptid_t' to 'pid_t'.  Call
	'linux_ptrace_attach_fail_reason_1' and
	'linux_ptrace_restricted_fail_reason'.
	(linux_ptrace_attach_fail_reason_lwp): New function.
	(linux_ptrace_me_fail_reason): New function.
	(linux_child_function): Handle ptrace error.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
	prototype.
	(linux_ptrace_attach_fail_reason_lwp): New prototype.
	(linux_ptrace_me_fail_reason): New prototype.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_ptrace_fun): Call
	'linux_ptrace_me_fail_reason'.
	(attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_attach): Call 'linux_ptrace_attach_fail_reason'.
	* thread-db.c (attach_thread): Call
	'linux_ptrace_attach_fail_reason_lwp'.
---
 gdb/doc/gdb.texinfo        | 138 +++++++++++++++++++++++++++++++++++++
 gdb/gdbserver/linux-low.c  |   7 +-
 gdb/gdbserver/thread-db.c  |   2 +-
 gdb/gdbsupport/gdb-dlfcn.c |   4 +-
 gdb/gdbsupport/gdb-dlfcn.h |   7 +-
 gdb/inf-ptrace.c           |  17 ++++-
 gdb/inf-ptrace.h           |  10 +++
 gdb/linux-nat.c            |   9 ++-
 gdb/nat/fork-inferior.c    |   4 +-
 gdb/nat/fork-inferior.h    |   7 +-
 gdb/nat/linux-ptrace.c     | 104 ++++++++++++++++++++++++++--
 gdb/nat/linux-ptrace.h     |  27 ++++++--
 12 files changed, 306 insertions(+), 30 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 53b7de91e4..16db62acac 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel ptrace restrictions::        Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -44689,6 +44692,141 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel ptrace restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} on GNU/Linux to,
+among other things, attach to a new or existing inferior in order to
+start debugging it.  Due to security concerns, some distributions and
+vendors disable or severely restrict the ability to perform these
+operations, which can make @value{GDBN} malfunction.  In this section,
+we will expand on how this malfunction can manifest itself, and how to
+modify the system's settings in order to be able to use @value{GDBN}
+properly.
+
+@menu
+* The GDB error message::               The error message displayed when the
+                                        system prevents @value{GDBN} from using
+                                        @code{ptrace}
+* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
+* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
+* Docker and seccomp::                  Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The GDB error message
+@appendixsection The @value{GDBN} error message
+
+When the system prevents @value{GDBN} from using the @code{ptrace}
+system call, you will likely see a descriptive error message
+explaining what is wrong and how to attempt to fix the problem.  For
+example, when SELinux's @code{deny_ptrace} option is enabled, you can
+see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing GDB
+from using 'ptrace'.  You can disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} can be affected
+by it.  You can temporarily disable the feature by doing, as root:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN}.  To do that, you can use the
+options @code{--cap-add=SYS_PTRACE --security-opt seccomp=unconfined}
+when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3113017ae6..45cf43ad9e 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -971,7 +971,8 @@ linux_ptrace_fun ()
 {
   if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
 	      (PTRACE_TYPE_ARG4) 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 linux_ptrace_me_fail_reason (errno).c_str ());
 
   if (setpgid (0, 0) < 0)
     trace_start_error_with_name ("setpgid");
@@ -1169,7 +1170,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1201,7 +1202,7 @@ linux_attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
       error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
     }
 
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index b2791b0513..cfba05977e 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -225,7 +225,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
diff --git a/gdb/gdbsupport/gdb-dlfcn.c b/gdb/gdbsupport/gdb-dlfcn.c
index 921f10f3d8..9e5a992c17 100644
--- a/gdb/gdbsupport/gdb-dlfcn.c
+++ b/gdb/gdbsupport/gdb-dlfcn.c
@@ -58,7 +58,7 @@ is_dl_available (void)
 #else /* NO_SHARED_LIB */
 
 gdb_dlhandle_up
-gdb_dlopen (const char *filename)
+gdb_dlopen (const char *filename, bool dont_throw)
 {
   void *result;
 #ifdef HAVE_DLFCN_H
@@ -66,7 +66,7 @@ gdb_dlopen (const char *filename)
 #elif __MINGW32__
   result = (void *) LoadLibrary (filename);
 #endif
-  if (result != NULL)
+  if (dont_throw || result != NULL)
     return gdb_dlhandle_up (result);
 
 #ifdef HAVE_DLFCN_H
diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
index 6a39d38941..a8ddbc03da 100644
--- a/gdb/gdbsupport/gdb-dlfcn.h
+++ b/gdb/gdbsupport/gdb-dlfcn.h
@@ -32,10 +32,11 @@ struct dlclose_deleter
 typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
 
 /* Load the dynamic library file named FILENAME, and return a handle
-   for that dynamic library.  Return NULL if the loading fails for any
-   reason.  */
+   for that dynamic library.  If the loading fails, return NULL if
+   DONT_THROW is true, or throw an exception otherwise (default
+   behaviour).  */
 
-gdb_dlhandle_up gdb_dlopen (const char *filename);
+gdb_dlhandle_up gdb_dlopen (const char *filename, bool dont_throw = false);
 
 /* Return the address of the symbol named SYMBOL inside the shared
    library whose handle is HANDLE.  Return NULL when the symbol could
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 4a8e732373..b792af00d1 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -94,6 +94,20 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 #endif /* PT_GET_PROCESS_STATE */
 \f
 
+/* Default method for "inf_ptrace_me_fail_reason", which returns an
+   empty string.  */
+
+static std::string
+default_inf_ptrace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* See inf-ptrace.h.  */
+
+std::string (*inf_ptrace_me_fail_reason) (int err)
+  = default_inf_ptrace_me_fail_reason;
+
 /* Prepare to be traced.  */
 
 static void
@@ -101,7 +115,8 @@ inf_ptrace_me (void)
 {
   /* "Trace me, Dr. Memory!"  */
   if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 inf_ptrace_me_fail_reason (errno).c_str ());
 }
 
 /* Start a new inferior Unix child process.  EXEC_FILE is the file to
diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
index 98b5d2e09e..7cdab9af89 100644
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -83,4 +83,14 @@ protected:
 
 extern pid_t get_ptrace_pid (ptid_t);
 
+/* Pointer to "inf_ptrace_me_fail_reason", which implements a function
+   that can be called by "inf_ptrace_me" in order to obtain the reason
+   for a ptrace failure.  ERR is the ERRNO value set by the failing
+   ptrace call.
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when ptrace fails (see linux-nat.c, for
+   example).  */
+extern std::string (*inf_ptrace_me_fail_reason) (int err);
+
 #endif
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 945c19f666..22cb55e6dc 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1136,7 +1136,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
@@ -1191,8 +1191,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
@@ -4696,6 +4697,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 355e9bef43..2ead4a4858 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index 1d0519fb26..7e6b889210 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
 #endif /* NAT_FORK_INFERIOR_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index c1ebc0a032..0bbbe8d67a 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,9 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/filestuff.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -30,11 +33,72 @@
    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 *);
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid)
+/* 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.1", true);
+
+  if (handle != nullptr)
+    {
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  setsebool deny_ptrace off\n"));
+    }
+
+  gdb_file_up yama_ptrace_scope
+    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
+
+  if (yama_ptrace_scope != nullptr)
+    {
+      char yama_scope = fgetc (yama_ptrace_scope.get ());
+
+      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'.  You can 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;
+}
+
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
+
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -56,10 +120,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+
+  if (!ptrace_restrict.empty ())
+    result += "\n" + ptrace_restrict;
+
+  return result;
+}
+
+/* See linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
@@ -68,6 +146,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 +407,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..90afb60f34 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,12 +176,27 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
-extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
-
-/* 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);
+/* 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);
+
+/* Find all possible reasons we could have failed to attach to PID's
+   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
+   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
+   this function should be used when attaching to an LWP other than
+   the leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
-- 
2.21.0

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 19:31         ` Sergio Durigan Junior
@ 2019-09-04 19:58           ` Pedro Alves
  2019-09-04 20:21             ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-04 19:58 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/4/19 8:31 PM, Sergio Durigan Junior wrote:
> I forgot to say, but the way my patch works now prevents the code path
> above to be executed.  My patch catches the ptrace failure very early in
> gdbserver initialization the process, when linux_child_function is
> called during the execution of linux_check_ptrace_features.  Since
> linux_child_function will try to perform a PTRACE_TRACEME (and fail if
> there are any restrictions in place), gdbserver will error out before it
> even tries to spawn/attach.
> 
> Nevertheless, during my tests I bypassed linux_check_ptrace_features and
> confirmed that the error message from linux_attach (above) is correctly
> displayed:
> 
>   [sergio@fedora-rawhide build]$ sleep 120 &
>   [5] 2732388
>   [sergio@fedora-rawhide build]$ ./gdb/gdbserver/gdbserver :9911 --attach 2732388
>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2732391 No such process
>   Cannot attach to process 2732388: 
> 
>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>   from using 'ptrace'.  You can disable it by executing (as root):
> 
>     setsebool deny_ptrace off
> 
>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>   GDB from using 'ptrace'.  You can disable it by executing (as root):
> 
>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
> 
>   Exiting

So if you _don't_ hack linux_check_ptrace_features, what does the user
see, with either

        gdbserver [OPTIONS] --attach COMM PID

or extended-remote + "(gdb) attach" ?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 19:58           ` Pedro Alves
@ 2019-09-04 20:21             ` Sergio Durigan Junior
  2019-09-04 20:35               ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 20:21 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Wednesday, September 04 2019, Pedro Alves wrote:

> On 9/4/19 8:31 PM, Sergio Durigan Junior wrote:
>> I forgot to say, but the way my patch works now prevents the code path
>> above to be executed.  My patch catches the ptrace failure very early in
>> gdbserver initialization the process, when linux_child_function is
>> called during the execution of linux_check_ptrace_features.  Since
>> linux_child_function will try to perform a PTRACE_TRACEME (and fail if
>> there are any restrictions in place), gdbserver will error out before it
>> even tries to spawn/attach.
>> 
>> Nevertheless, during my tests I bypassed linux_check_ptrace_features and
>> confirmed that the error message from linux_attach (above) is correctly
>> displayed:
>> 
>>   [sergio@fedora-rawhide build]$ sleep 120 &
>>   [5] 2732388
>>   [sergio@fedora-rawhide build]$ ./gdb/gdbserver/gdbserver :9911 --attach 2732388
>>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2732391 No such process
>>   Cannot attach to process 2732388: 
>> 
>>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>>   from using 'ptrace'.  You can disable it by executing (as root):
>> 
>>     setsebool deny_ptrace off
>> 
>>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>>   GDB from using 'ptrace'.  You can disable it by executing (as root):
>> 
>>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
>> 
>>   Exiting
>
> So if you _don't_ hack linux_check_ptrace_features, what does the user
> see, with either
>
>         gdbserver [OPTIONS] --attach COMM PID

Basically the same thing:

  [sergio@fedora-rawhide build]$ ./gdb/gdbserver/gdbserver :9911 --attach 2733600
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2733602 No such process
  gdbserver: Could not trace the inferior process.
  gdbserver: ptrace: Operation not permitted
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  linux_check_ptrace_features: waitpid: unexpected status 32512.
  Exiting

The only difference is that we don't see the "Cannot attach to process
PID:" message.

> or extended-remote + "(gdb) attach" ?

I'm trying to come up with a way to test this.  The only way would be to
make gdbserver successfully start so that we could perform an
extended-remote connection from GDB.  However, if gdbserver starts
without problems, this means that the ptrace check succeeded and that
there are no ptrace restrictions in place.  Therefore, an "attach" would
succeed as well.  Which means that even if we're running GDB in a
ptrace-restricted system, things will go OK as long as gdbserver is not
restricted.  In this case, we wouldn't see any error messages IIUC.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 20:21             ` Sergio Durigan Junior
@ 2019-09-04 20:35               ` Pedro Alves
  2019-09-04 20:56                 ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-04 20:35 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/4/19 9:21 PM, Sergio Durigan Junior wrote:
>> or extended-remote + "(gdb) attach" ?
> I'm trying to come up with a way to test this.  The only way would be to
> make gdbserver successfully start so that we could perform an
> extended-remote connection from GDB.  However, if gdbserver starts
> without problems, this means that the ptrace check succeeded and that
> there are no ptrace restrictions in place.  Therefore, an "attach" would
> succeed as well.  Which means that even if we're running GDB in a
> ptrace-restricted system, things will go OK as long as gdbserver is not
> restricted.  In this case, we wouldn't see any error messages IIUC.

So

 $ gdbserver --multi :9999

exits with error immediately?

You could start gdbserver with the restrictions off (like a long
lived daemon), and then while gdbserver is running enable
restrictions, I suppose.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 20:35               ` Pedro Alves
@ 2019-09-04 20:56                 ` Sergio Durigan Junior
  2019-09-04 21:23                   ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 20:56 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Wednesday, September 04 2019, Pedro Alves wrote:

> On 9/4/19 9:21 PM, Sergio Durigan Junior wrote:
>>> or extended-remote + "(gdb) attach" ?
>> I'm trying to come up with a way to test this.  The only way would be to
>> make gdbserver successfully start so that we could perform an
>> extended-remote connection from GDB.  However, if gdbserver starts
>> without problems, this means that the ptrace check succeeded and that
>> there are no ptrace restrictions in place.  Therefore, an "attach" would
>> succeed as well.  Which means that even if we're running GDB in a
>> ptrace-restricted system, things will go OK as long as gdbserver is not
>> restricted.  In this case, we wouldn't see any error messages IIUC.
>
> So
>
>  $ gdbserver --multi :9999
>
> exits with error immediately?

Yeah.

> You could start gdbserver with the restrictions off (like a long
> lived daemon), and then while gdbserver is running enable
> restrictions, I suppose.

Ah, right, I think that would also work.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 20:56                 ` Sergio Durigan Junior
@ 2019-09-04 21:23                   ` Pedro Alves
  2019-09-04 21:36                     ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-04 21:23 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/4/19 9:56 PM, Sergio Durigan Junior wrote:
>> You could start gdbserver with the restrictions off (like a long
>> lived daemon), and then while gdbserver is running enable
>> restrictions, I suppose.
> Ah, right, I think that would also work.

I'm interested in knowing whether the error percolates to
the gdb side in a reasonable form.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 21:23                   ` Pedro Alves
@ 2019-09-04 21:36                     ` Sergio Durigan Junior
  2019-09-05 12:19                       ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-04 21:36 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Wednesday, September 04 2019, Pedro Alves wrote:

> On 9/4/19 9:56 PM, Sergio Durigan Junior wrote:
>>> You could start gdbserver with the restrictions off (like a long
>>> lived daemon), and then while gdbserver is running enable
>>> restrictions, I suppose.
>> Ah, right, I think that would also work.
>
> I'm interested in knowing whether the error percolates to
> the gdb side in a reasonable form.

The attach error is displayed by GDB, but the information about ptrace
restrictions is not.

This is what I see on GDB:

  (gdb) attach 32378
  Attaching to process 32378
  Attaching to process 32378 failed

And this is what I see on gdbserver:

  gdbserver: Cannot attach to process 32378: Permission denied (13), the SELinux boolean 'deny_ptrace' is enabled, you can disable this process attach protection by: (gdb) shell sudo setsebool deny_ptrace=0


I think there's something wrong with the terminal settings because
newlines aren't being respected here.  But the message is still there,
and the user can still understand it, I think.

Ideally we'd have GDB also display the ptrace restriction warning, but I
think that if the user can see that the attach failed, she will likely
look at what happened with gdbserver, and will see the error there.  If
we were to show the message on GDB, we'd also have to rewrite it in a
way that explains that the command needs to be run at the target, which
may confuse things.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-04 21:36                     ` Sergio Durigan Junior
@ 2019-09-05 12:19                       ` Pedro Alves
  2019-09-05 17:58                         ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-05 12:19 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/4/19 10:36 PM, Sergio Durigan Junior wrote:
> On Wednesday, September 04 2019, Pedro Alves wrote:
> 
>> On 9/4/19 9:56 PM, Sergio Durigan Junior wrote:
>>>> You could start gdbserver with the restrictions off (like a long
>>>> lived daemon), and then while gdbserver is running enable
>>>> restrictions, I suppose.
>>> Ah, right, I think that would also work.
>>
>> I'm interested in knowing whether the error percolates to
>> the gdb side in a reasonable form.
> 
> The attach error is displayed by GDB, but the information about ptrace
> restrictions is not.
> 
> This is what I see on GDB:
> 
>   (gdb) attach 32378
>   Attaching to process 32378
>   Attaching to process 32378 failed
> 
> And this is what I see on gdbserver:
> 
>   gdbserver: Cannot attach to process 32378: Permission denied (13), the SELinux boolean 'deny_ptrace' is enabled, you can disable this process attach protection by: (gdb) shell sudo setsebool deny_ptrace=0
> 
> 
> I think there's something wrong with the terminal settings because
> newlines aren't being respected here.  But the message is still there,
> and the user can still understand it, I think.

Odd.  We should fix that...

But this made me realize something.  Here:

  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 ());
 +

This is code that is run by the fork child.  Recall that we 
had introduced trace_start_error_with_name instead of using
perror_with_name / error, because between after fork and before
exec, we can only safely use async-signal-safe functions.

But that linux_ptrace_me_fail_reason call is doing a lot of
non-async-signal safe things...

I think the right approach would be to make the child pass back the errno
value to the parent, and then have the parent, do the dlopens, the
std::string/malloc uses, etc. and print the error messages.

The traditional way to pass data around like this is via a pipe.
darwin-nat.c uses a pipe around fork, see ptrace_fds, though for a
different reason.

> Ideally we'd have GDB also display the ptrace restriction warning, but I
> think that if the user can see that the attach failed, she will likely
> look at what happened with gdbserver, and will see the error there.  

I don't think that assuming that the user has easy/convenient access
to gdbserver's terminal is a great assumption.  Consider IDEs automating
things, or gdbserver really being used as a service in a container, etc.

Note that at least for Yama, you can have ptrace allow PTRACE_ME, while
PTRACE_ATTACH gets refused.  So it's not guaranteed that gdbserver would
exit out early on start up, meaning, I think that handling this
gdbserver + "(gdb) attach" error case isn't being pedantic.

> If
> we were to show the message on GDB, we'd also have to rewrite it in a
> way that explains that the command needs to be run at the target, which
> may confuse things.
Or it may not confuse things?  I don't think that should prevent
improving things.  Surely we can find some way to express the message
without causing confusion.  I'm not sure it does, but if it does,
maybe we can just append a "on the target system" somewhere?

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v3] Improve ptrace-error detection on Linux targets
  2019-09-04 19:54 ` [PATCH v3] " Sergio Durigan Junior
@ 2019-09-05 17:04   ` Eli Zaretskii
  0 siblings, 0 replies; 98+ messages in thread
From: Eli Zaretskii @ 2019-09-05 17:04 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: gdb-patches, palves, b7.10110111

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Ruslan Kabatsayev <b7.10110111@gmail.com>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Wed,  4 Sep 2019 15:54:08 -0400
> 
> Changes since v2:
> 
> - Fixed nits pointed by Pedro.  Incorporated his patch to improve the
>   linux_ptrace_attach_fail_reason_* funtions.
> 
> - Fixed documentation issues.

OK for the documentation part.

Thanks.

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2] Improve ptrace-error detection on Linux targets
  2019-09-05 12:19                       ` Pedro Alves
@ 2019-09-05 17:58                         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-05 17:58 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, September 05 2019, Pedro Alves wrote:

> On 9/4/19 10:36 PM, Sergio Durigan Junior wrote:
>> On Wednesday, September 04 2019, Pedro Alves wrote:
>> 
>>> On 9/4/19 9:56 PM, Sergio Durigan Junior wrote:
>>>>> You could start gdbserver with the restrictions off (like a long
>>>>> lived daemon), and then while gdbserver is running enable
>>>>> restrictions, I suppose.
>>>> Ah, right, I think that would also work.
>>>
>>> I'm interested in knowing whether the error percolates to
>>> the gdb side in a reasonable form.
>> 
>> The attach error is displayed by GDB, but the information about ptrace
>> restrictions is not.
>> 
>> This is what I see on GDB:
>> 
>>   (gdb) attach 32378
>>   Attaching to process 32378
>>   Attaching to process 32378 failed
>> 
>> And this is what I see on gdbserver:
>> 
>>   gdbserver: Cannot attach to process 32378: Permission denied (13),
>> the SELinux boolean 'deny_ptrace' is enabled, you can disable this
>> process attach protection by: (gdb) shell sudo setsebool
>> deny_ptrace=0
>> 
>> 
>> I think there's something wrong with the terminal settings because
>> newlines aren't being respected here.  But the message is still there,
>> and the user can still understand it, I think.
>
> Odd.  We should fix that...

OK, then.

> But this made me realize something.  Here:
>
>   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 ());
>  +
>
> This is code that is run by the fork child.  Recall that we 
> had introduced trace_start_error_with_name instead of using
> perror_with_name / error, because between after fork and before
> exec, we can only safely use async-signal-safe functions.

Ah, I didn't remember the exact reason, thanks for refreshing my
memory.

> But that linux_ptrace_me_fail_reason call is doing a lot of
> non-async-signal safe things...

Fair enough.

> I think the right approach would be to make the child pass back the errno
> value to the parent, and then have the parent, do the dlopens, the
> std::string/malloc uses, etc. and print the error messages.
>
> The traditional way to pass data around like this is via a pipe.
> darwin-nat.c uses a pipe around fork, see ptrace_fds, though for a
> different reason.

Funny, at first (when I was trying out the patch) I noticed that this
ptrace call was failing, but thought that, because it is being called
inside the child, it would be good if we could pass errno back to the
parent, as you said.  But then I just decided to go the easy way and
call trace_start_error_with_name directly there.

I'll look into passing the errno back to the parent via pipes, as
suggested.

>> Ideally we'd have GDB also display the ptrace restriction warning, but I
>> think that if the user can see that the attach failed, she will likely
>> look at what happened with gdbserver, and will see the error there.  
>
> I don't think that assuming that the user has easy/convenient access
> to gdbserver's terminal is a great assumption.  Consider IDEs automating
> things, or gdbserver really being used as a service in a container, etc.
>
> Note that at least for Yama, you can have ptrace allow PTRACE_ME, while
> PTRACE_ATTACH gets refused.  So it's not guaranteed that gdbserver would
> exit out early on start up, meaning, I think that handling this
> gdbserver + "(gdb) attach" error case isn't being pedantic.
>
>> If
>> we were to show the message on GDB, we'd also have to rewrite it in a
>> way that explains that the command needs to be run at the target, which
>> may confuse things.
> Or it may not confuse things?  I don't think that should prevent
> improving things.  Surely we can find some way to express the message
> without causing confusion.  I'm not sure it does, but if it does,
> maybe we can just append a "on the target system" somewhere?

"on the target system" can be confusing for newcomers.  But sure, I can
certainly find a way to write the warning in a non-confusing way.  I'm
just thinking about the extra work involved to implement this.  I'll
design something here and send a v4 when ready.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
                   ` (4 preceding siblings ...)
  2019-09-04 19:54 ` [PATCH v3] " Sergio Durigan Junior
@ 2019-09-11  1:11 ` Sergio Durigan Junior
  2019-09-12 12:39   ` Eli Zaretskii
  2019-09-24 20:40   ` Tom Tromey
  2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
  6 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-11  1:11 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev, Sergio Durigan Junior

Changes from v3:

- Implement a better way to check for the ptrace errno inside
  linux_fork_to_function et al.

- Implement error message passing (gdbserver -> GDB) when attach fails
  on gdbserver.

- Extend docs a little bit to mention that the ptrace restrictions
  apply also to gdbserver (very trivial).

- Improve error message to instruct the user to perform the necessary
  actions in the target side when remote debugging is in play.

- Very minor nits.


In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_TRACEME will
fail with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

My first thought (and attempt) was to make GDB print a generic warning
when a ptrace error happened; this message would just point the user
to our documentation, where she could find more information about
possible causes for the error (and try to diagnose/fix the problem).
This proved to be too simple, and I was convinced that it is actually
a good idea to go the extra kilometre and try to pinpoint the specific
problem (or problems) preventing ptrace from working, as well as
provide useful suggestions on how the user can fix things.

Here is the patch I came up with.  It implements a new function,
'linux_ptrace_restricted_fail_reason', which does a few things to
check what's wrong with ptrace:

  - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
    option is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /usr/bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  During startup program exited with code 127.
  (gdb)

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /usr/bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  During startup program exited with code 127.
  (gdb)

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Permission denied
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2766868 No such process
  gdbserver: Could not trace the inferior process.
  gdbserver: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  #

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.
  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).

This means that the patch expands our documentation and creates a new
appendix section named "Linux kernel ptrace restrictions", with
sub-sections for each possible restriction that might be in place.

Notice how, on every message, we instruct the user to "do the right
thing" if gdbserver is being used.  This is because if the user
started gdbserver *before* any ptrace restriction was in place, and
then, for some reason, one or more restrictions get enabled, then the
error message will be displayed both on gdbserver *and* on the
connected GDB.  Since the user will be piloting GDB, it's important to
explicitly say that the ptrace restrictions are enabled in the target,
where gdbserver is running.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

I tested this patch locally, on my Fedora 30 machine (actually, a
Fedora Rawhide VM), but I'm not proposing a testcase for it because of
the difficulty of writing one.

WDYT?

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* gdbsupport/gdb-dlfcn.c (gdb_dlopen): Add argument 'dont_throw'
	Don't throw if it's true.
	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Add optional argument
	'dont_throw'.  Update comment.
	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
	function.
	(inf_ptrace_me_fail_reason): New variable.
	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
	* linux-nat.c (attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_nat_target::attach): Update call to
	'linux_ptrace_attach_fail_reason'.
	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
	* nat/fork-inferior.c (trace_start_error_with_name): Add
	optional 'append' argument.
	* nat/fork-inferior.h (trace_start_error_with_name): Update
	prototype.
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
	"gdbsupport/filestuff.h" and "nat/fork-inferior.h".
	(selinux_ftype): New typedef.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_1): New function.
	(linux_ptrace_attach_fail_reason): Change first argument type
	from 'ptid_t' to 'pid_t'.  Call
	'linux_ptrace_attach_fail_reason_1' and
	'linux_ptrace_restricted_fail_reason'.
	(linux_ptrace_attach_fail_reason_lwp): New function.
	(linux_ptrace_me_fail_reason): New function.
	(errno_pipe): New variable.
	(linux_fork_to_function): Initialize pipe before forking.
	(linux_child_function): Deal with errno-passing from child.
	Handle ptrace error.
	(linux_check_child_ptrace_errno): New function.
	(linux_check_child_ptrace_errno): Call
	'linux_check_child_ptrace_errno'.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
	prototype.
	(linux_ptrace_attach_fail_reason_lwp): New prototype.
	(linux_ptrace_me_fail_reason): New prototype.
	* remote.c (extended_remote_target::attach): Handle error
	message passed by the server when attach fails.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_ptrace_fun): Call
	'linux_ptrace_me_fail_reason'.
	(attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_attach): Call 'linux_ptrace_attach_fail_reason'.
	* server.c (handle_v_attach): Use try..catch when calling
	'attach_inferior', and send an error message to the client
	when needed.
	* thread-db.c (attach_thread): Call
	'linux_ptrace_attach_fail_reason_lwp'.
---
 gdb/doc/gdb.texinfo        | 143 +++++++++++++++++++++++++++++
 gdb/gdbserver/linux-low.c  |   9 +-
 gdb/gdbserver/server.c     |  14 ++-
 gdb/gdbserver/thread-db.c  |   2 +-
 gdb/gdbsupport/gdb-dlfcn.c |   4 +-
 gdb/gdbsupport/gdb-dlfcn.h |   7 +-
 gdb/inf-ptrace.c           |  17 +++-
 gdb/inf-ptrace.h           |  10 ++
 gdb/linux-nat.c            |   9 +-
 gdb/nat/fork-inferior.c    |   4 +-
 gdb/nat/fork-inferior.h    |   7 +-
 gdb/nat/linux-ptrace.c     | 183 +++++++++++++++++++++++++++++++++++--
 gdb/nat/linux-ptrace.h     |  27 ++++--
 gdb/remote.c               |  16 +++-
 14 files changed, 418 insertions(+), 34 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 53b7de91e4..ffaedc4ba8 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel ptrace restrictions::        Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -44689,6 +44692,146 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} and
+@code{gdbserver} on GNU/Linux to, among other things, attach to a new
+or existing inferior in order to start debugging it.  Due to security
+concerns, some distributions and vendors disable or severely restrict
+the ability to perform these operations, which can make @value{GDBN}
+or @code{gdbserver} malfunction.  In this section, we will expand on
+how this malfunction can manifest itself, and how to modify the
+system's settings in order to be able to use @value{GDBN} and
+@code{gdbserver} properly.
+
+@menu
+* The error message::                   The error message displayed when the
+                                        system prevents @value{GDBN}
+                                        or @code{gdbserver} from using
+                                        @code{ptrace}
+* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
+* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
+* Docker and seccomp::                  Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The error message
+@appendixsection The error message
+
+When the system prevents @value{GDBN} or @code{gdbserver} from using
+the @code{ptrace} system call, you will likely see a descriptive error
+message explaining what is wrong and how to attempt to fix the
+problem.  For example, when SELinux's @code{deny_ptrace} option is
+enabled, you can see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing GDB
+from using 'ptrace'.  You can disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+If you are debugging the inferior remotely, the instruction(s) above must
+be performed in the target system (e.g., where GDBserver is running).
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} or
+@code{gdbserver} can be affected by it.  You can temporarily disable
+the feature by doing, as root:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN} or @code{gdbserver}.  To do that,
+you can use the options @code{--cap-add=SYS_PTRACE --security-opt
+seccomp=unconfined} when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index 3113017ae6..2b3c586125 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -971,7 +971,8 @@ linux_ptrace_fun ()
 {
   if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
 	      (PTRACE_TYPE_ARG4) 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 linux_ptrace_me_fail_reason (errno).c_str ());
 
   if (setpgid (0, 0) < 0)
     trace_start_error_with_name ("setpgid");
@@ -1169,7 +1170,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1201,8 +1202,8 @@ linux_attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
-      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
+      error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
     }
 
   /* Don't ignore the initial SIGSTOP if we just attached to this
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 127cd3840b..51a7066594 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2913,9 +2913,21 @@ handle_v_attach (char *own_buf)
 {
   client_state &cs = get_client_state ();
   int pid;
+  int ret;
 
   pid = strtol (own_buf + 8, NULL, 16);
-  if (pid != 0 && attach_inferior (pid) == 0)
+
+  try
+    {
+      ret = attach_inferior (pid);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      sprintf (own_buf, "E.%s", e.what ());
+      return 0;
+    }
+
+  if (pid != 0 && ret == 0)
     {
       /* Don't report shared library events after attaching, even if
 	 some libraries are preloaded.  GDB will always poll the
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index b2791b0513..cfba05977e 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -225,7 +225,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
diff --git a/gdb/gdbsupport/gdb-dlfcn.c b/gdb/gdbsupport/gdb-dlfcn.c
index 921f10f3d8..9e5a992c17 100644
--- a/gdb/gdbsupport/gdb-dlfcn.c
+++ b/gdb/gdbsupport/gdb-dlfcn.c
@@ -58,7 +58,7 @@ is_dl_available (void)
 #else /* NO_SHARED_LIB */
 
 gdb_dlhandle_up
-gdb_dlopen (const char *filename)
+gdb_dlopen (const char *filename, bool dont_throw)
 {
   void *result;
 #ifdef HAVE_DLFCN_H
@@ -66,7 +66,7 @@ gdb_dlopen (const char *filename)
 #elif __MINGW32__
   result = (void *) LoadLibrary (filename);
 #endif
-  if (result != NULL)
+  if (dont_throw || result != NULL)
     return gdb_dlhandle_up (result);
 
 #ifdef HAVE_DLFCN_H
diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
index 6a39d38941..a8ddbc03da 100644
--- a/gdb/gdbsupport/gdb-dlfcn.h
+++ b/gdb/gdbsupport/gdb-dlfcn.h
@@ -32,10 +32,11 @@ struct dlclose_deleter
 typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
 
 /* Load the dynamic library file named FILENAME, and return a handle
-   for that dynamic library.  Return NULL if the loading fails for any
-   reason.  */
+   for that dynamic library.  If the loading fails, return NULL if
+   DONT_THROW is true, or throw an exception otherwise (default
+   behaviour).  */
 
-gdb_dlhandle_up gdb_dlopen (const char *filename);
+gdb_dlhandle_up gdb_dlopen (const char *filename, bool dont_throw = false);
 
 /* Return the address of the symbol named SYMBOL inside the shared
    library whose handle is HANDLE.  Return NULL when the symbol could
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 4a8e732373..b792af00d1 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -94,6 +94,20 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 #endif /* PT_GET_PROCESS_STATE */
 \f
 
+/* Default method for "inf_ptrace_me_fail_reason", which returns an
+   empty string.  */
+
+static std::string
+default_inf_ptrace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* See inf-ptrace.h.  */
+
+std::string (*inf_ptrace_me_fail_reason) (int err)
+  = default_inf_ptrace_me_fail_reason;
+
 /* Prepare to be traced.  */
 
 static void
@@ -101,7 +115,8 @@ inf_ptrace_me (void)
 {
   /* "Trace me, Dr. Memory!"  */
   if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 inf_ptrace_me_fail_reason (errno).c_str ());
 }
 
 /* Start a new inferior Unix child process.  EXEC_FILE is the file to
diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
index 98b5d2e09e..7cdab9af89 100644
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -83,4 +83,14 @@ protected:
 
 extern pid_t get_ptrace_pid (ptid_t);
 
+/* Pointer to "inf_ptrace_me_fail_reason", which implements a function
+   that can be called by "inf_ptrace_me" in order to obtain the reason
+   for a ptrace failure.  ERR is the ERRNO value set by the failing
+   ptrace call.
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when ptrace fails (see linux-nat.c, for
+   example).  */
+extern std::string (*inf_ptrace_me_fail_reason) (int err);
+
 #endif
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 945c19f666..22cb55e6dc 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1136,7 +1136,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
@@ -1191,8 +1191,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
@@ -4696,6 +4697,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 355e9bef43..2ead4a4858 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index 1d0519fb26..7e6b889210 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
 #endif /* NAT_FORK_INFERIOR_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index c1ebc0a032..694f390de0 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,9 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/filestuff.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -30,11 +33,85 @@
    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 *);
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid)
+/* 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.1", true);
+
+  if (handle != nullptr)
+    {
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  setsebool deny_ptrace off\n"));
+    }
+
+  gdb_file_up yama_ptrace_scope
+    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
+
+  if (yama_ptrace_scope != nullptr)
+    {
+      char yama_scope = fgetc (yama_ptrace_scope.get ());
+
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
+    }
+
+  if (ret.empty ())
+    {
+      /* It wasn't possible to determine the exact reason for the
+	 ptrace error.  Let's just emit a generic error message
+	 pointing the user to our documentation, where she can find
+	 instructions on how to try to diagnose the problem.  */
+      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.");
+    }
+
+  /* The user may be debugging remotely, so we have to warn that
+     the instructions above should be performed in the target.  */
+  string_appendf (ret,
+		  _("\n\
+If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
+be disabled in the target system (e.g., where GDBserver is running)."));
+
+  return ret;
+}
+
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
+
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -56,10 +133,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+
+  if (!ptrace_restrict.empty ())
+    result += "\n" + ptrace_restrict;
+
+  return result;
+}
+
+/* See linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
@@ -68,6 +159,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.  */
@@ -257,6 +356,12 @@ linux_ptrace_test_ret_to_nx (void)
 #endif /* defined __i386__ || defined __x86_64__ */
 }
 
+/* If the PTRACE_TRACEME call on linux_child_function errors, we need
+   to be able to send ERRNO back to the parent so that it can check
+   whether there are restrictions in place preventing ptrace from
+   working.  We do that with a pipe.  */
+static int errno_pipe[2];
+
 /* Helper function to fork a process and make the child process call
    the function FUNCTION, passing CHILD_STACK as parameter.
 
@@ -273,6 +378,11 @@ linux_fork_to_function (gdb_byte *child_stack, int (*function) (void *))
   /* Sanity check the function pointer.  */
   gdb_assert (function != NULL);
 
+  /* Create the pipe that will be used by the child to pass ERRNO
+     after the PTRACE_TRACEME call.  */
+  if (pipe (errno_pipe) != 0)
+    trace_start_error_with_name ("pipe");
+
 #if defined(__UCLIBC__) && defined(HAS_NOMMU)
 #define STACK_SIZE 4096
 
@@ -321,7 +431,21 @@ 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);
+  /* Close read end.  */
+  close (errno_pipe[0]);
+
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = errno;
+
+  /* Write ERRNO to the pipe, even if it's zero, and close the writing
+     end of the pipe.  */
+  write (errno_pipe[1], &ptrace_errno, sizeof (ptrace_errno));
+  close (errno_pipe[1]);
+
+  if (ret != 0)
+    _exit (0);
+
   kill (getpid (), SIGSTOP);
 
   /* Fork a grandchild.  */
@@ -336,6 +460,48 @@ static void linux_test_for_tracesysgood (int child_pid);
 static void linux_test_for_tracefork (int child_pid);
 static void linux_test_for_exitkill (int child_pid);
 
+/* Helper function to wait for the child to send us the ptrace ERRNO,
+   and check if it's OK.  */
+
+static void
+linux_check_child_ptrace_errno ()
+{
+  int child_errno;
+  fd_set rset;
+  struct timeval timeout;
+
+  /* Close the writing end of the pipe.  */
+  close (errno_pipe[1]);
+
+  FD_ZERO (&rset);
+  FD_SET (errno_pipe[0], &rset);
+
+  /* One second should be plenty of time to wait for the child's
+     reply.  */
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 0;
+
+  int ret = select (errno_pipe[0] + 1, &rset, NULL, NULL, &timeout);
+
+  if (ret < 0)
+    trace_start_error_with_name ("select");
+  else if (ret == 0)
+    error (_("Timeout while waiting for child's ptrace errno"));
+  else
+    read (errno_pipe[0], &child_errno, sizeof (child_errno));
+
+  if (child_errno != 0)
+    {
+      /* The child can't use PTRACE_TRACEME.  We just bail out.  */
+      std::string reason = linux_ptrace_restricted_fail_reason (child_errno);
+
+      errno = child_errno;
+      trace_start_error_with_name ("ptrace", reason.c_str ());
+    }
+
+  close (errno_pipe[0]);
+}
+
 /* Determine ptrace features available on this target.  */
 
 void
@@ -352,6 +518,9 @@ linux_check_ptrace_features (void)
      reporting.  */
   child_pid = linux_fork_to_function (NULL, linux_child_function);
 
+  /* Check if the child can successfully use ptrace.  */
+  linux_check_child_ptrace_errno ();
+
   ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name (("waitpid"));
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index fd2f12a342..90afb60f34 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,12 +176,27 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
-extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
-
-/* 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);
+/* 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);
+
+/* Find all possible reasons we could have failed to attach to PID's
+   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
+   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
+   this function should be used when attaching to an LWP other than
+   the leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
diff --git a/gdb/remote.c b/gdb/remote.c
index ae06c4ba79..e5780bf586 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5831,8 +5831,20 @@ extended_remote_target::attach (const char *args, int from_tty)
     case PACKET_UNKNOWN:
       error (_("This target does not support attaching to a process"));
     default:
-      error (_("Attaching to %s failed"),
-	     target_pid_to_str (ptid_t (pid)).c_str ());
+      {
+	std::string errmsg = rs->buf.data ();
+
+	if (!errmsg.empty ())
+	  {
+	    /* Get rid of the "E." prefix.  */
+	    errmsg.erase (0, 2);
+	  }
+
+	error (_("Attaching to %s failed%s%s"),
+	       target_pid_to_str (ptid_t (pid)).c_str (),
+	       !errmsg.empty () ? "\n" : "",
+	       errmsg.c_str ());
+      }
     }
 
   set_current_inferior (remote_add_inferior (false, pid, 1, 0));
-- 
2.21.0

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-11  1:11 ` [PATCH v4] " Sergio Durigan Junior
@ 2019-09-12 12:39   ` Eli Zaretskii
  2019-09-12 18:29     ` Sergio Durigan Junior
  2019-09-24 20:40   ` Tom Tromey
  1 sibling, 1 reply; 98+ messages in thread
From: Eli Zaretskii @ 2019-09-12 12:39 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: gdb-patches, palves, b7.10110111

> From: Sergio Durigan Junior <sergiodj@redhat.com>
> Cc: Pedro Alves <palves@redhat.com>,
> 	Eli Zaretskii <eliz@gnu.org>,
> 	Ruslan Kabatsayev <b7.10110111@gmail.com>,
> 	Sergio Durigan Junior <sergiodj@redhat.com>
> Date: Tue, 10 Sep 2019 21:11:03 -0400
> 
> +The SELinux 'deny_ptrace' option is enabled and preventing GDB
                                                              ^^^
@value{GDBN}

Other than that, the documentation part is OK.

Thanks.

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-12 12:39   ` Eli Zaretskii
@ 2019-09-12 18:29     ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-12 18:29 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: gdb-patches, palves, b7.10110111

On Thursday, September 12 2019, Eli Zaretskii wrote:

>> From: Sergio Durigan Junior <sergiodj@redhat.com>
>> Cc: Pedro Alves <palves@redhat.com>,
>> 	Eli Zaretskii <eliz@gnu.org>,
>> 	Ruslan Kabatsayev <b7.10110111@gmail.com>,
>> 	Sergio Durigan Junior <sergiodj@redhat.com>
>> Date: Tue, 10 Sep 2019 21:11:03 -0400
>> 
>> +The SELinux 'deny_ptrace' option is enabled and preventing GDB
>                                                               ^^^
> @value{GDBN}
>
> Other than that, the documentation part is OK.

Thanks, fixed.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-11  1:11 ` [PATCH v4] " Sergio Durigan Junior
  2019-09-12 12:39   ` Eli Zaretskii
@ 2019-09-24 20:40   ` Tom Tromey
  2019-09-25 14:14     ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2019-09-24 20:40 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

Sergio> Changes from v3:

Thanks for doing this.  I like this change quite a bit.  For one thing I
think it would have saved me some time over the years, as I randomly
forget about ptrace security measures on new machines.

Sergio> +++ b/gdb/gdbserver/server.c
Sergio> @@ -2913,9 +2913,21 @@ handle_v_attach (char *own_buf)
Sergio>  {
Sergio>    client_state &cs = get_client_state ();
Sergio>    int pid;
Sergio> +  int ret;
 
Sergio>    pid = strtol (own_buf + 8, NULL, 16);
Sergio> -  if (pid != 0 && attach_inferior (pid) == 0)
Sergio> +
Sergio> +  try
Sergio> +    {
Sergio> +      ret = attach_inferior (pid);
Sergio> +    }
Sergio> +  catch (const gdb_exception_error &e)
Sergio> +    {
Sergio> +      sprintf (own_buf, "E.%s", e.what ());

Unrestricted sprintf gives me pause.  Do we know own_buf is large
enough?  Or can/should we truncate the text instead?

Sergio> -gdb_dlopen (const char *filename)
Sergio> +gdb_dlopen (const char *filename, bool dont_throw)

This parameter is only used in one spot, and it's on an error path that
is computing the string to show to the user.  So, I think it's better to
just remove the parameter and use try/catch in that one caller.

Sergio> +static std::string
Sergio> +linux_ptrace_restricted_fail_reason (int err)
Sergio> +{
...
Sergio> +  std::string ret;
Sergio> +  gdb_dlhandle_up handle = gdb_dlopen ("libselinux.so.1", true);
Sergio> +
Sergio> +  if (handle != nullptr)
Sergio> +    {
Sergio> +      selinux_ftype selinux_get_bool
Sergio> +	= (selinux_ftype) gdb_dlsym (handle, "security_get_boolean_active");
Sergio> +
Sergio> +      if (selinux_get_bool != NULL
Sergio> +	  && (*selinux_get_bool) ("deny_ptrace") == 1)
Sergio> +	string_appendf (ret,
Sergio> +			_("\n\
Sergio> +The SELinux 'deny_ptrace' option is enabled and preventing GDB\n\
Sergio> +from using 'ptrace'.  You can disable it by executing (as root):\n\
Sergio> +\n\
Sergio> +  setsebool deny_ptrace off\n"));
Sergio> +    }
Sergio> +
Sergio> +  gdb_file_up yama_ptrace_scope
Sergio> +    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
...

If SELinux was the problem, is it also possible that ptrace_scope is the
problem?

I was wondering if each case should just return, or if checking each one
is the correct thing to do here.

thanks,
Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-24 20:40   ` Tom Tromey
@ 2019-09-25 14:14     ` Sergio Durigan Junior
  2019-09-25 22:04       ` Tom Tromey
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-25 14:14 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Tuesday, September 24 2019, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
> Sergio> Changes from v3:
>
> Thanks for doing this.  I like this change quite a bit.  For one thing I
> think it would have saved me some time over the years, as I randomly
> forget about ptrace security measures on new machines.

:-)

Thanks for the review.

> Sergio> +++ b/gdb/gdbserver/server.c
> Sergio> @@ -2913,9 +2913,21 @@ handle_v_attach (char *own_buf)
> Sergio>  {
> Sergio>    client_state &cs = get_client_state ();
> Sergio>    int pid;
> Sergio> +  int ret;
>  
> Sergio>    pid = strtol (own_buf + 8, NULL, 16);
> Sergio> -  if (pid != 0 && attach_inferior (pid) == 0)
> Sergio> +
> Sergio> +  try
> Sergio> +    {
> Sergio> +      ret = attach_inferior (pid);
> Sergio> +    }
> Sergio> +  catch (const gdb_exception_error &e)
> Sergio> +    {
> Sergio> +      sprintf (own_buf, "E.%s", e.what ());
>
> Unrestricted sprintf gives me pause.  Do we know own_buf is large
> enough?  Or can/should we truncate the text instead?

I was also worried about this.  I found other cases using sprintf, but
their strings are not as big.  I know own_buf comes from struct
client_state, and its size of PBUFSIZ + 1.  One option would be to use
snprintf with this value.  WDYT?

> Sergio> -gdb_dlopen (const char *filename)
> Sergio> +gdb_dlopen (const char *filename, bool dont_throw)
>
> This parameter is only used in one spot, and it's on an error path that
> is computing the string to show to the user.  So, I think it's better to
> just remove the parameter and use try/catch in that one caller.

OK, I will do that.

> Sergio> +static std::string
> Sergio> +linux_ptrace_restricted_fail_reason (int err)
> Sergio> +{
> ...
> Sergio> +  std::string ret;
> Sergio> +  gdb_dlhandle_up handle = gdb_dlopen ("libselinux.so.1", true);
> Sergio> +
> Sergio> +  if (handle != nullptr)
> Sergio> +    {
> Sergio> +      selinux_ftype selinux_get_bool
> Sergio> +	= (selinux_ftype) gdb_dlsym (handle, "security_get_boolean_active");
> Sergio> +
> Sergio> +      if (selinux_get_bool != NULL
> Sergio> +	  && (*selinux_get_bool) ("deny_ptrace") == 1)
> Sergio> +	string_appendf (ret,
> Sergio> +			_("\n\
> Sergio> +The SELinux 'deny_ptrace' option is enabled and preventing GDB\n\
> Sergio> +from using 'ptrace'.  You can disable it by executing (as root):\n\
> Sergio> +\n\
> Sergio> +  setsebool deny_ptrace off\n"));
> Sergio> +    }
> Sergio> +
> Sergio> +  gdb_file_up yama_ptrace_scope
> Sergio> +    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
> ...
>
> If SELinux was the problem, is it also possible that ptrace_scope is the
> problem?

Not at the same time, but if both restrictions are active, we can be
sure that both will have an impact when using GDB.

> I was wondering if each case should just return, or if checking each one
> is the correct thing to do here.

I don't have a strong opinion here, but I think it's more useful for the
user if we print everything wrong in the first pass.  I'm thinking of
cases when starting GDB may take a while, for example: if we tell the
user a list of problems that need to be solved, then she won't need to
keep retrying.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-25 14:14     ` Sergio Durigan Junior
@ 2019-09-25 22:04       ` Tom Tromey
  2019-09-26  4:22         ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2019-09-25 22:04 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: Tom Tromey, GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

>> Unrestricted sprintf gives me pause.  Do we know own_buf is large
>> enough?  Or can/should we truncate the text instead?

Sergio> I was also worried about this.  I found other cases using sprintf, but
Sergio> their strings are not as big.  I know own_buf comes from struct
Sergio> client_state, and its size of PBUFSIZ + 1.  One option would be to use
Sergio> snprintf with this value.  WDYT?

I think that would be fine.

>> I was wondering if each case should just return, or if checking each one
>> is the correct thing to do here.

Sergio> I don't have a strong opinion here, but I think it's more useful for the
Sergio> user if we print everything wrong in the first pass.  I'm thinking of
Sergio> cases when starting GDB may take a while, for example: if we tell the
Sergio> user a list of problems that need to be solved, then she won't need to
Sergio> keep retrying.

Makes sense to me, thanks.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v4] Improve ptrace-error detection on Linux targets
  2019-09-25 22:04       ` Tom Tromey
@ 2019-09-26  4:22         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26  4:22 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Wednesday, September 25 2019, Tom Tromey wrote:

>>> Unrestricted sprintf gives me pause.  Do we know own_buf is large
>>> enough?  Or can/should we truncate the text instead?
>
> Sergio> I was also worried about this.  I found other cases using sprintf, but
> Sergio> their strings are not as big.  I know own_buf comes from struct
> Sergio> client_state, and its size of PBUFSIZ + 1.  One option would be to use
> Sergio> snprintf with this value.  WDYT?
>
> I think that would be fine.
>
>>> I was wondering if each case should just return, or if checking each one
>>> is the correct thing to do here.
>
> Sergio> I don't have a strong opinion here, but I think it's more useful for the
> Sergio> user if we print everything wrong in the first pass.  I'm thinking of
> Sergio> cases when starting GDB may take a while, for example: if we tell the
> Sergio> user a list of problems that need to be solved, then she won't need to
> Sergio> keep retrying.
>
> Makes sense to me, thanks.

Thanks; I've just sent v5 now.

Cheers,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
                   ` (5 preceding siblings ...)
  2019-09-11  1:11 ` [PATCH v4] " Sergio Durigan Junior
@ 2019-09-26  4:22 ` Sergio Durigan Junior
  2019-09-26 17:32   ` Tom Tromey
                     ` (2 more replies)
  6 siblings, 3 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26  4:22 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev, Tom Tromey,
	Sergio Durigan Junior

Changes from v4:

- Don't add 'dont_throw' optional argument to gdb_dlopen; use
  conventional try..catch.

- Use snprintf when printing to 'own_buf' and make sure we don't
  overflow the buffer.



In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_TRACEME will
fail with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

My first thought (and attempt) was to make GDB print a generic warning
when a ptrace error happened; this message would just point the user
to our documentation, where she could find more information about
possible causes for the error (and try to diagnose/fix the problem).
This proved to be too simple, and I was convinced that it is actually
a good idea to go the extra kilometre and try to pinpoint the specific
problem (or problems) preventing ptrace from working, as well as
provide useful suggestions on how the user can fix things.

Here is the patch I came up with.  It implements a new function,
'linux_ptrace_restricted_fail_reason', which does a few things to
check what's wrong with ptrace:

  - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
    option is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /usr/bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  During startup program exited with code 127.
  (gdb)

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /usr/bin/true
  ...
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Operation not permitted
  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  During startup program exited with code 127.
  (gdb)

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Permission denied
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2766868 No such process
  gdbserver: Could not trace the inferior process.
  gdbserver: ptrace: Permission denied
  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  #

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.
  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).

This means that the patch expands our documentation and creates a new
appendix section named "Linux kernel ptrace restrictions", with
sub-sections for each possible restriction that might be in place.

Notice how, on every message, we instruct the user to "do the right
thing" if gdbserver is being used.  This is because if the user
started gdbserver *before* any ptrace restriction was in place, and
then, for some reason, one or more restrictions get enabled, then the
error message will be displayed both on gdbserver *and* on the
connected GDB.  Since the user will be piloting GDB, it's important to
explicitly say that the ptrace restrictions are enabled in the target,
where gdbserver is running.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

I tested this patch locally, on my Fedora 30 machine (actually, a
Fedora Rawhide VM), but I'm not proposing a testcase for it because of
the difficulty of writing one.

WDYT?

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Jan Kratochvil  <jan.kratochvil@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* gdbsupport/gdb-dlfcn.h (gdb_dlopen): Update comment and
	mention that the function throws an error.
	* inf-ptrace.c (default_inf_ptrace_me_fail_reason): New
	function.
	(inf_ptrace_me_fail_reason): New variable.
	(inf_ptrace_me): Update call to 'trace_start_error_with_name'.
	* inf-ptrace.h (inf_ptrace_me_fail_reason): New variable.
	* linux-nat.c (attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_nat_target::attach): Update call to
	'linux_ptrace_attach_fail_reason'.
	(_initialize_linux_nat): Set 'inf_ptrace_me_fail_reason'.
	* nat/fork-inferior.c (trace_start_error_with_name): Add
	optional 'append' argument.
	* nat/fork-inferior.h (trace_start_error_with_name): Update
	prototype.
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
	"gdbsupport/filestuff.h" and "nat/fork-inferior.h".
	(selinux_ftype): New typedef.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_1): New function.
	(linux_ptrace_attach_fail_reason): Change first argument type
	from 'ptid_t' to 'pid_t'.  Call
	'linux_ptrace_attach_fail_reason_1' and
	'linux_ptrace_restricted_fail_reason'.
	(linux_ptrace_attach_fail_reason_lwp): New function.
	(linux_ptrace_me_fail_reason): New function.
	(errno_pipe): New variable.
	(linux_fork_to_function): Initialize pipe before forking.
	(linux_child_function): Deal with errno-passing from child.
	Handle ptrace error.
	(linux_check_child_ptrace_errno): New function.
	(linux_check_child_ptrace_errno): Call
	'linux_check_child_ptrace_errno'.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): Update
	prototype.
	(linux_ptrace_attach_fail_reason_lwp): New prototype.
	(linux_ptrace_me_fail_reason): New prototype.
	* remote.c (extended_remote_target::attach): Handle error
	message passed by the server when attach fails.

gdb/gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
	    Pedro Alves  <palves@redhat.com>

	* linux-low.c (linux_ptrace_fun): Call
	'linux_ptrace_me_fail_reason'.
	(attach_proc_task_lwp_callback): Call
	'linux_ptrace_attach_fail_reason_lwp'.
	(linux_attach): Call 'linux_ptrace_attach_fail_reason'.
	* server.c (handle_v_attach): Use try..catch when calling
	'attach_inferior', and send an error message to the client
	when needed.
	* thread-db.c (attach_thread): Call
	'linux_ptrace_attach_fail_reason_lwp'.
---
 gdb/doc/gdb.texinfo        | 143 +++++++++++++++++++++++++++
 gdb/gdbserver/linux-low.c  |   9 +-
 gdb/gdbserver/server.c     |  14 ++-
 gdb/gdbserver/thread-db.c  |   2 +-
 gdb/gdbsupport/gdb-dlfcn.h |   4 +-
 gdb/inf-ptrace.c           |  17 +++-
 gdb/inf-ptrace.h           |  10 ++
 gdb/linux-nat.c            |   9 +-
 gdb/nat/fork-inferior.c    |   4 +-
 gdb/nat/fork-inferior.h    |   7 +-
 gdb/nat/linux-ptrace.c     | 192 +++++++++++++++++++++++++++++++++++--
 gdb/nat/linux-ptrace.h     |  27 ++++--
 gdb/remote.c               |  16 +++-
 13 files changed, 423 insertions(+), 31 deletions(-)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f2713c0396..e7b5b18f2b 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel ptrace restrictions::        Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -44665,6 +44668,146 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} and
+@code{gdbserver} on GNU/Linux to, among other things, attach to a new
+or existing inferior in order to start debugging it.  Due to security
+concerns, some distributions and vendors disable or severely restrict
+the ability to perform these operations, which can make @value{GDBN}
+or @code{gdbserver} malfunction.  In this section, we will expand on
+how this malfunction can manifest itself, and how to modify the
+system's settings in order to be able to use @value{GDBN} and
+@code{gdbserver} properly.
+
+@menu
+* The error message::                   The error message displayed when the
+                                        system prevents @value{GDBN}
+                                        or @code{gdbserver} from using
+                                        @code{ptrace}
+* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
+* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
+* Docker and seccomp::                  Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The error message
+@appendixsection The error message
+
+When the system prevents @value{GDBN} or @code{gdbserver} from using
+the @code{ptrace} system call, you will likely see a descriptive error
+message explaining what is wrong and how to attempt to fix the
+problem.  For example, when SELinux's @code{deny_ptrace} option is
+enabled, you can see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing @value{GDBN}
+from using 'ptrace'.  You can disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+If you are debugging the inferior remotely, the instruction(s) above must
+be performed in the target system (e.g., where GDBserver is running).
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} or
+@code{gdbserver} can be affected by it.  You can temporarily disable
+the feature by doing, as root:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN} or @code{gdbserver}.  To do that,
+you can use the options @code{--cap-add=SYS_PTRACE --security-opt
+seccomp=unconfined} when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
index d64c3641ff..c0e15c122f 100644
--- a/gdb/gdbserver/linux-low.c
+++ b/gdb/gdbserver/linux-low.c
@@ -967,7 +967,8 @@ linux_ptrace_fun ()
 {
   if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
 	      (PTRACE_TYPE_ARG4) 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 linux_ptrace_me_fail_reason (errno).c_str ());
 
   if (setpgid (0, 0) < 0)
     trace_start_error_with_name ("setpgid");
@@ -1165,7 +1166,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1197,8 +1198,8 @@ linux_attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
-      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
+      error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
     }
 
   /* Don't ignore the initial SIGSTOP if we just attached to this
diff --git a/gdb/gdbserver/server.c b/gdb/gdbserver/server.c
index 67e8e3e54d..976ecbd2df 100644
--- a/gdb/gdbserver/server.c
+++ b/gdb/gdbserver/server.c
@@ -2893,9 +2893,21 @@ handle_v_attach (char *own_buf)
 {
   client_state &cs = get_client_state ();
   int pid;
+  int ret;
 
   pid = strtol (own_buf + 8, NULL, 16);
-  if (pid != 0 && attach_inferior (pid) == 0)
+
+  try
+    {
+      ret = attach_inferior (pid);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
+      return 0;
+    }
+
+  if (pid != 0 && ret == 0)
     {
       /* Don't report shared library events after attaching, even if
 	 some libraries are preloaded.  GDB will always poll the
diff --git a/gdb/gdbserver/thread-db.c b/gdb/gdbserver/thread-db.c
index c6b43a06cc..e3acf83850 100644
--- a/gdb/gdbserver/thread-db.c
+++ b/gdb/gdbserver/thread-db.c
@@ -224,7 +224,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
diff --git a/gdb/gdbsupport/gdb-dlfcn.h b/gdb/gdbsupport/gdb-dlfcn.h
index 6a39d38941..e933b7a473 100644
--- a/gdb/gdbsupport/gdb-dlfcn.h
+++ b/gdb/gdbsupport/gdb-dlfcn.h
@@ -32,8 +32,8 @@ struct dlclose_deleter
 typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
 
 /* Load the dynamic library file named FILENAME, and return a handle
-   for that dynamic library.  Return NULL if the loading fails for any
-   reason.  */
+   for that dynamic library.  Throw an error if the loading fails for
+   any reason.  */
 
 gdb_dlhandle_up gdb_dlopen (const char *filename);
 
diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index 4a8e732373..b792af00d1 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -94,6 +94,20 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 #endif /* PT_GET_PROCESS_STATE */
 \f
 
+/* Default method for "inf_ptrace_me_fail_reason", which returns an
+   empty string.  */
+
+static std::string
+default_inf_ptrace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* See inf-ptrace.h.  */
+
+std::string (*inf_ptrace_me_fail_reason) (int err)
+  = default_inf_ptrace_me_fail_reason;
+
 /* Prepare to be traced.  */
 
 static void
@@ -101,7 +115,8 @@ inf_ptrace_me (void)
 {
   /* "Trace me, Dr. Memory!"  */
   if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
-    trace_start_error_with_name ("ptrace");
+    trace_start_error_with_name ("ptrace",
+				 inf_ptrace_me_fail_reason (errno).c_str ());
 }
 
 /* Start a new inferior Unix child process.  EXEC_FILE is the file to
diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
index 98b5d2e09e..7cdab9af89 100644
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -83,4 +83,14 @@ protected:
 
 extern pid_t get_ptrace_pid (ptid_t);
 
+/* Pointer to "inf_ptrace_me_fail_reason", which implements a function
+   that can be called by "inf_ptrace_me" in order to obtain the reason
+   for a ptrace failure.  ERR is the ERRNO value set by the failing
+   ptrace call.
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when ptrace fails (see linux-nat.c, for
+   example).  */
+extern std::string (*inf_ptrace_me_fail_reason) (int err);
+
 #endif
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index cd5cf1830d..2c7ded7043 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -1132,7 +1132,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
@@ -1187,8 +1187,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
@@ -4567,6 +4568,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 355e9bef43..2ead4a4858 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -591,7 +591,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index 065496c382..405f2cf548 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -98,9 +98,10 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
 #endif /* NAT_FORK_INFERIOR_H */
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index c1ebc0a032..8a048d2ec9 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,9 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/filestuff.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -30,11 +33,94 @@
    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 *);
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid)
+/* 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;
+
+  try
+    {
+      handle = gdb_dlopen ("libselinux.so.1");
+    }
+  catch (const gdb_exception_error &e)
+    {
+      handle.reset (nullptr);
+    }
+
+  if (handle != nullptr)
+    {
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  setsebool deny_ptrace off\n"));
+    }
+
+  gdb_file_up yama_ptrace_scope
+    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
+
+  if (yama_ptrace_scope != nullptr)
+    {
+      char yama_scope = fgetc (yama_ptrace_scope.get ());
+
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
+    }
+
+  if (ret.empty ())
+    {
+      /* It wasn't possible to determine the exact reason for the
+	 ptrace error.  Let's just emit a generic error message
+	 pointing the user to our documentation, where she can find
+	 instructions on how to try to diagnose the problem.  */
+      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.");
+    }
+
+  /* The user may be debugging remotely, so we have to warn that
+     the instructions above should be performed in the target.  */
+  string_appendf (ret,
+		  _("\n\
+If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
+be disabled in the target system (e.g., where GDBserver is running)."));
+
+  return ret;
+}
+
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
+
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -56,10 +142,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+
+  if (!ptrace_restrict.empty ())
+    result += "\n" + ptrace_restrict;
+
+  return result;
+}
+
+/* See linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
@@ -68,6 +168,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.  */
@@ -257,6 +365,12 @@ linux_ptrace_test_ret_to_nx (void)
 #endif /* defined __i386__ || defined __x86_64__ */
 }
 
+/* If the PTRACE_TRACEME call on linux_child_function errors, we need
+   to be able to send ERRNO back to the parent so that it can check
+   whether there are restrictions in place preventing ptrace from
+   working.  We do that with a pipe.  */
+static int errno_pipe[2];
+
 /* Helper function to fork a process and make the child process call
    the function FUNCTION, passing CHILD_STACK as parameter.
 
@@ -273,6 +387,11 @@ linux_fork_to_function (gdb_byte *child_stack, int (*function) (void *))
   /* Sanity check the function pointer.  */
   gdb_assert (function != NULL);
 
+  /* Create the pipe that will be used by the child to pass ERRNO
+     after the PTRACE_TRACEME call.  */
+  if (pipe (errno_pipe) != 0)
+    trace_start_error_with_name ("pipe");
+
 #if defined(__UCLIBC__) && defined(HAS_NOMMU)
 #define STACK_SIZE 4096
 
@@ -321,7 +440,21 @@ 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);
+  /* Close read end.  */
+  close (errno_pipe[0]);
+
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = errno;
+
+  /* Write ERRNO to the pipe, even if it's zero, and close the writing
+     end of the pipe.  */
+  write (errno_pipe[1], &ptrace_errno, sizeof (ptrace_errno));
+  close (errno_pipe[1]);
+
+  if (ret != 0)
+    _exit (0);
+
   kill (getpid (), SIGSTOP);
 
   /* Fork a grandchild.  */
@@ -336,6 +469,48 @@ static void linux_test_for_tracesysgood (int child_pid);
 static void linux_test_for_tracefork (int child_pid);
 static void linux_test_for_exitkill (int child_pid);
 
+/* Helper function to wait for the child to send us the ptrace ERRNO,
+   and check if it's OK.  */
+
+static void
+linux_check_child_ptrace_errno ()
+{
+  int child_errno;
+  fd_set rset;
+  struct timeval timeout;
+
+  /* Close the writing end of the pipe.  */
+  close (errno_pipe[1]);
+
+  FD_ZERO (&rset);
+  FD_SET (errno_pipe[0], &rset);
+
+  /* One second should be plenty of time to wait for the child's
+     reply.  */
+  timeout.tv_sec = 1;
+  timeout.tv_usec = 0;
+
+  int ret = select (errno_pipe[0] + 1, &rset, NULL, NULL, &timeout);
+
+  if (ret < 0)
+    trace_start_error_with_name ("select");
+  else if (ret == 0)
+    error (_("Timeout while waiting for child's ptrace errno"));
+  else
+    read (errno_pipe[0], &child_errno, sizeof (child_errno));
+
+  if (child_errno != 0)
+    {
+      /* The child can't use PTRACE_TRACEME.  We just bail out.  */
+      std::string reason = linux_ptrace_restricted_fail_reason (child_errno);
+
+      errno = child_errno;
+      trace_start_error_with_name ("ptrace", reason.c_str ());
+    }
+
+  close (errno_pipe[0]);
+}
+
 /* Determine ptrace features available on this target.  */
 
 void
@@ -352,6 +527,9 @@ linux_check_ptrace_features (void)
      reporting.  */
   child_pid = linux_fork_to_function (NULL, linux_child_function);
 
+  /* Check if the child can successfully use ptrace.  */
+  linux_check_child_ptrace_errno ();
+
   ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name (("waitpid"));
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index fd2f12a342..90afb60f34 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,12 +176,27 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
-extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
-
-/* 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);
+/* 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);
+
+/* Find all possible reasons we could have failed to attach to PID's
+   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
+   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
+   this function should be used when attaching to an LWP other than
+   the leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
diff --git a/gdb/remote.c b/gdb/remote.c
index 21160e13ac..efc5084cfe 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5825,8 +5825,20 @@ extended_remote_target::attach (const char *args, int from_tty)
     case PACKET_UNKNOWN:
       error (_("This target does not support attaching to a process"));
     default:
-      error (_("Attaching to %s failed"),
-	     target_pid_to_str (ptid_t (pid)).c_str ());
+      {
+	std::string errmsg = rs->buf.data ();
+
+	if (!errmsg.empty ())
+	  {
+	    /* Get rid of the "E." prefix.  */
+	    errmsg.erase (0, 2);
+	  }
+
+	error (_("Attaching to %s failed%s%s"),
+	       target_pid_to_str (ptid_t (pid)).c_str (),
+	       !errmsg.empty () ? "\n" : "",
+	       errmsg.c_str ());
+      }
     }
 
   set_current_inferior (remote_add_inferior (false, pid, 1, 0));
-- 
2.21.0

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
@ 2019-09-26 17:32   ` Tom Tromey
  2019-09-26 17:48     ` Pedro Alves
  2019-09-26 17:50     ` Sergio Durigan Junior
  2019-09-26 18:13   ` Pedro Alves
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
  2 siblings, 2 replies; 98+ messages in thread
From: Tom Tromey @ 2019-09-26 17:32 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev, Tom Tromey

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

Sergio> Changes from v4:
Sergio> - Don't add 'dont_throw' optional argument to gdb_dlopen; use
Sergio>   conventional try..catch.

Sergio> - Use snprintf when printing to 'own_buf' and make sure we don't
Sergio>   overflow the buffer.

Thank you for doing this.
I think this is ok, please check it in.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 17:32   ` Tom Tromey
@ 2019-09-26 17:48     ` Pedro Alves
  2019-09-26 17:51       ` Sergio Durigan Junior
  2019-09-26 17:50     ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-26 17:48 UTC (permalink / raw)
  To: Tom Tromey, Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/26/19 6:32 PM, Tom Tromey wrote:
>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
> 
> Sergio> Changes from v4:
> Sergio> - Don't add 'dont_throw' optional argument to gdb_dlopen; use
> Sergio>   conventional try..catch.
> 
> Sergio> - Use snprintf when printing to 'own_buf' and make sure we don't
> Sergio>   overflow the buffer.
> 
> Thank you for doing this.
> I think this is ok, please check it in.

Please give me a bit to review before merging.  I think I spotted an issue,
but I haven't had a chance to take a look at it since before Cauldron.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 17:32   ` Tom Tromey
  2019-09-26 17:48     ` Pedro Alves
@ 2019-09-26 17:50     ` Sergio Durigan Junior
  1 sibling, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26 17:50 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, September 26 2019, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
> Sergio> Changes from v4:
> Sergio> - Don't add 'dont_throw' optional argument to gdb_dlopen; use
> Sergio>   conventional try..catch.
>
> Sergio> - Use snprintf when printing to 'own_buf' and make sure we don't
> Sergio>   overflow the buffer.
>
> Thank you for doing this.
> I think this is ok, please check it in.

Thanks for the extensive reviews, Tom and Pedro.

Pushed: 381beca6146ac68b57edf47d28cdb335fbd11635

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 17:48     ` Pedro Alves
@ 2019-09-26 17:51       ` Sergio Durigan Junior
  2019-09-26 18:14         ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26 17:51 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, September 26 2019, Pedro Alves wrote:

> On 9/26/19 6:32 PM, Tom Tromey wrote:
>>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>> 
>> Sergio> Changes from v4:
>> Sergio> - Don't add 'dont_throw' optional argument to gdb_dlopen; use
>> Sergio>   conventional try..catch.
>> 
>> Sergio> - Use snprintf when printing to 'own_buf' and make sure we don't
>> Sergio>   overflow the buffer.
>> 
>> Thank you for doing this.
>> I think this is ok, please check it in.
>
> Please give me a bit to review before merging.  I think I spotted an issue,
> but I haven't had a chance to take a look at it since before Cauldron.

Ops, I just saw this message now, after pushing the patch.  Would you
like me to revert it?

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
  2019-09-26 17:32   ` Tom Tromey
@ 2019-09-26 18:13   ` Pedro Alves
  2019-09-26 18:23     ` Sergio Durigan Junior
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
  2 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-26 18:13 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Eli Zaretskii, Ruslan Kabatsayev, Tom Tromey

On 9/26/19 5:21 AM, Sergio Durigan Junior wrote:

> +
> +@smallexample
> +$ gdb program
> +...
> +Starting program: program
> +warning: Could not trace the inferior process.
> +Error:
> +warning: ptrace: Permission denied

This "Error:" above is stale, right?

> +There might be restrictions preventing ptrace from working.  Please see
> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
> +for more details.
> +During startup program exited with code 127.
> +(@value{GDBP})
> +@end smallexample
> +

> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
> index d64c3641ff..c0e15c122f 100644
> --- a/gdb/gdbserver/linux-low.c
> +++ b/gdb/gdbserver/linux-low.c
> @@ -967,7 +967,8 @@ linux_ptrace_fun ()
>  {
>    if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
>  	      (PTRACE_TYPE_ARG4) 0) < 0)
> -    trace_start_error_with_name ("ptrace");
> +    trace_start_error_with_name ("ptrace",
> +				 linux_ptrace_me_fail_reason (errno).c_str ());

This code path is also run by a fork child.  So it needs the same pipe
treatment pointed out in v2 review, due to linux_ptrace_me_fail_reason not
being async-signal-safe.  It does not make sense to do the treatment in one
place and not on others.

>  /* Prepare to be traced.  */
>  
>  static void
> @@ -101,7 +115,8 @@ inf_ptrace_me (void)
>  {
>    /* "Trace me, Dr. Memory!"  */
>    if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
> -    trace_start_error_with_name ("ptrace");
> +    trace_start_error_with_name ("ptrace",
> +				 inf_ptrace_me_fail_reason (errno).c_str ());

Same here...

>  }
>  
>  /* Start a new inferior Unix child process.  EXEC_FILE is the file to
> diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
> index 98b5d2e09e..7cdab9af89 100644
> --- a/gdb/inf-ptrace.h
> +++ b/gdb/inf-ptrace.h
> @@ -83,4 +83,14 @@ protected:
>  
>  extern pid_t get_ptrace_pid (ptid_t);
>  
> +/* Pointer to "inf_ptrace_me_fail_reason", 

As pointed out before, this part of the comment does not make sense.
"inf_ptrace_me_fail_reason" is the name of the variable!

It's the same as saying this:

 /* Pointer to "ptr".  */
 int *ptr;

which is of course bogus.

Just say something like:

  /* Pointer to function that can be called by "inf_ptrace_me" (...)


which implements a function
> +   that can be called by "inf_ptrace_me" in order to obtain the reason
> +   for a ptrace failure.  ERR is the ERRNO value set by the failing
> +   ptrace call.
> +
> +   This pointer can be overriden by targets that want to personalize
> +   the error message printed when ptrace fails (see linux-nat.c, for
> +   example).  */
> +extern std::string (*inf_ptrace_me_fail_reason) (int err);
> +
>  #endif
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index cd5cf1830d..2c7ded7043 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -1132,7 +1132,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>  	  else
>  	    {
>  	      std::string reason
> -		= linux_ptrace_attach_fail_reason_string (ptid, err);
> +		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>  	      warning (_("Cannot attach to lwp %d: %s"),
>  		       lwpid, reason.c_str ());
> @@ -1187,8 +1187,9 @@ linux_nat_target::attach (const char *args, int from_tty)
>      }
>    catch (const gdb_exception_error &ex)
>      {
> +      int saved_errno = errno;
>        pid_t pid = parse_pid_to_attach (args);
> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>  
>        if (!reason.empty ())
>  	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
> @@ -4567,6 +4568,10 @@ Enables printf debugging output."),
>    sigemptyset (&blocked_mask);
>  
>    lwp_lwpid_htab_create ();
> +
> +  /* Set the proper function to generate a message when ptrace
> +     fails.  */
> +  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
>  }
>  \f
>  

> +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;
> +
> +  try
> +    {
> +      handle = gdb_dlopen ("libselinux.so.1");
> +    }
> +  catch (const gdb_exception_error &e)
> +    {
> +      handle.reset (nullptr);

Nit, this line is unnecessary.  If an exception was thrown,
then handle was not writen to by the try block.

> +    }
> +

> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -5825,8 +5825,20 @@ extended_remote_target::attach (const char *args, int from_tty)
>      case PACKET_UNKNOWN:
>        error (_("This target does not support attaching to a process"));
>      default:
> -      error (_("Attaching to %s failed"),
> -	     target_pid_to_str (ptid_t (pid)).c_str ());
> +      {
> +	std::string errmsg = rs->buf.data ();
> +
> +	if (!errmsg.empty ())
> +	  {
> +	    /* Get rid of the "E." prefix.  */
> +	    errmsg.erase (0, 2);
> +	  }
> +

What if the server just sends a regular numeric error, like "E01"?

> +	error (_("Attaching to %s failed%s%s"),
> +	       target_pid_to_str (ptid_t (pid)).c_str (),
> +	       !errmsg.empty () ? "\n" : "",
> +	       errmsg.c_str ());
> +      }
>      }
>  
>    set_current_inferior (remote_add_inferior (false, pid, 1, 0));
> 

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 17:51       ` Sergio Durigan Junior
@ 2019-09-26 18:14         ` Pedro Alves
  2019-09-26 18:25           ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2019-09-26 18:14 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 9/26/19 6:51 PM, Sergio Durigan Junior wrote:
> On Thursday, September 26 2019, Pedro Alves wrote:

>> Please give me a bit to review before merging.  I think I spotted an issue,
>> but I haven't had a chance to take a look at it since before Cauldron.
> 
> Ops, I just saw this message now, after pushing the patch.  Would you
> like me to revert it?

Yes, since it just went in, I think it would be better to just revert
it, and work on a new full patch.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 18:13   ` Pedro Alves
@ 2019-09-26 18:23     ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26 18:23 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev, Tom Tromey

On Thursday, September 26 2019, Pedro Alves wrote:

> On 9/26/19 5:21 AM, Sergio Durigan Junior wrote:
>
>> +
>> +@smallexample
>> +$ gdb program
>> +...
>> +Starting program: program
>> +warning: Could not trace the inferior process.
>> +Error:
>> +warning: ptrace: Permission denied
>
> This "Error:" above is stale, right?

In the documentation, yes.  This is not being output anymore in the
code.  I removed it from the docs now.

>> +There might be restrictions preventing ptrace from working.  Please see
>> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>> +for more details.
>> +During startup program exited with code 127.
>> +(@value{GDBP})
>> +@end smallexample
>> +
>
>> diff --git a/gdb/gdbserver/linux-low.c b/gdb/gdbserver/linux-low.c
>> index d64c3641ff..c0e15c122f 100644
>> --- a/gdb/gdbserver/linux-low.c
>> +++ b/gdb/gdbserver/linux-low.c
>> @@ -967,7 +967,8 @@ linux_ptrace_fun ()
>>  {
>>    if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
>>  	      (PTRACE_TYPE_ARG4) 0) < 0)
>> -    trace_start_error_with_name ("ptrace");
>> +    trace_start_error_with_name ("ptrace",
>> +				 linux_ptrace_me_fail_reason (errno).c_str ());
>
> This code path is also run by a fork child.  So it needs the same pipe
> treatment pointed out in v2 review, due to linux_ptrace_me_fail_reason not
> being async-signal-safe.  It does not make sense to do the treatment in one
> place and not on others.

OK, I will do that.

>>  /* Prepare to be traced.  */
>>  
>>  static void
>> @@ -101,7 +115,8 @@ inf_ptrace_me (void)
>>  {
>>    /* "Trace me, Dr. Memory!"  */
>>    if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
>> -    trace_start_error_with_name ("ptrace");
>> +    trace_start_error_with_name ("ptrace",
>> +				 inf_ptrace_me_fail_reason (errno).c_str ());
>
> Same here...

Likewise.

>>  }
>>  
>>  /* Start a new inferior Unix child process.  EXEC_FILE is the file to
>> diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
>> index 98b5d2e09e..7cdab9af89 100644
>> --- a/gdb/inf-ptrace.h
>> +++ b/gdb/inf-ptrace.h
>> @@ -83,4 +83,14 @@ protected:
>>  
>>  extern pid_t get_ptrace_pid (ptid_t);
>>  
>> +/* Pointer to "inf_ptrace_me_fail_reason", 
>
> As pointed out before, this part of the comment does not make sense.
> "inf_ptrace_me_fail_reason" is the name of the variable!
>
> It's the same as saying this:
>
>  /* Pointer to "ptr".  */
>  int *ptr;
>
> which is of course bogus.
>
> Just say something like:
>
>   /* Pointer to function that can be called by "inf_ptrace_me" (...)
>
>
> which implements a function

Done.

>> +   that can be called by "inf_ptrace_me" in order to obtain the reason
>> +   for a ptrace failure.  ERR is the ERRNO value set by the failing
>> +   ptrace call.
>> +
>> +   This pointer can be overriden by targets that want to personalize
>> +   the error message printed when ptrace fails (see linux-nat.c, for
>> +   example).  */
>> +extern std::string (*inf_ptrace_me_fail_reason) (int err);
>> +
>>  #endif
>> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
>> index cd5cf1830d..2c7ded7043 100644
>> --- a/gdb/linux-nat.c
>> +++ b/gdb/linux-nat.c
>> @@ -1132,7 +1132,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>>  	  else
>>  	    {
>>  	      std::string reason
>> -		= linux_ptrace_attach_fail_reason_string (ptid, err);
>> +		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
>>  
>>  	      warning (_("Cannot attach to lwp %d: %s"),
>>  		       lwpid, reason.c_str ());
>> @@ -1187,8 +1187,9 @@ linux_nat_target::attach (const char *args, int from_tty)
>>      }
>>    catch (const gdb_exception_error &ex)
>>      {
>> +      int saved_errno = errno;
>>        pid_t pid = parse_pid_to_attach (args);
>> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
>> +      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>>  
>>        if (!reason.empty ())
>>  	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
>> @@ -4567,6 +4568,10 @@ Enables printf debugging output."),
>>    sigemptyset (&blocked_mask);
>>  
>>    lwp_lwpid_htab_create ();
>> +
>> +  /* Set the proper function to generate a message when ptrace
>> +     fails.  */
>> +  inf_ptrace_me_fail_reason = linux_ptrace_me_fail_reason;
>>  }
>>  \f
>>  
>
>> +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;
>> +
>> +  try
>> +    {
>> +      handle = gdb_dlopen ("libselinux.so.1");
>> +    }
>> +  catch (const gdb_exception_error &e)
>> +    {
>> +      handle.reset (nullptr);
>
> Nit, this line is unnecessary.  If an exception was thrown,
> then handle was not writen to by the try block.

Ah, I wasn't sure of it, so decided to be safe.  Thanks.

>> +    }
>> +
>
>> --- a/gdb/remote.c
>> +++ b/gdb/remote.c
>> @@ -5825,8 +5825,20 @@ extended_remote_target::attach (const char *args, int from_tty)
>>      case PACKET_UNKNOWN:
>>        error (_("This target does not support attaching to a process"));
>>      default:
>> -      error (_("Attaching to %s failed"),
>> -	     target_pid_to_str (ptid_t (pid)).c_str ());
>> +      {
>> +	std::string errmsg = rs->buf.data ();
>> +
>> +	if (!errmsg.empty ())
>> +	  {
>> +	    /* Get rid of the "E." prefix.  */
>> +	    errmsg.erase (0, 2);
>> +	  }
>> +
>
> What if the server just sends a regular numeric error, like "E01"?

Maybe I looked at wrong places, but I couldn't find a standard way to
deal with these error codes.  This doesn't seem to be documented.

I can extend the code to just remove the "E." prefix if the string
starts with "E.", but that seems hacky.  Is there a more recommended way
to do this?

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v5] Improve ptrace-error detection on Linux targets
  2019-09-26 18:14         ` Pedro Alves
@ 2019-09-26 18:25           ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2019-09-26 18:25 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Thursday, September 26 2019, Pedro Alves wrote:

> On 9/26/19 6:51 PM, Sergio Durigan Junior wrote:
>> On Thursday, September 26 2019, Pedro Alves wrote:
>
>>> Please give me a bit to review before merging.  I think I spotted an issue,
>>> but I haven't had a chance to take a look at it since before Cauldron.
>> 
>> Ops, I just saw this message now, after pushing the patch.  Would you
>> like me to revert it?
>
> Yes, since it just went in, I think it would be better to just revert
> it, and work on a new full patch.

OK, done.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 3/6] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
                       ` (4 preceding siblings ...)
  2020-02-26 20:06     ` [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
       [not found]     ` <87v9nh3yme.fsf@redhat.com>
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
  7 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

This patch is one important piece of the series.  It expands
'fork_inferior' in order to deal with new steps in the process of
initializing the inferior.  We now have to:

- Create a pipe that will be used to communicate with our
  fork (pre-exec), and which the fork will use to pass back to us the
  errno value of the 'traceme_fun' call.

- Close this pipe after it is used.

- Check the errno value passed back from the fork, and report any
  problems in the initialization to the user.

I thought about and implemented a few designs for all of this, but
ended up sticking with the function overload one.  'fork_inferior' is
now two functions: one that will take a traceme function like
'(*traceme_fun) ()' --- i.e., the original 'fork_inferior' behaviour,
and other that will take a function like '(*traceme_fun) (int
trace_pipe_write)'.  Depending on which function it takes, we know
whether the user does not want us to check whether the 'traceme_fun'
call was successful (former) or if she does (latter).

All in all, the patch is not complicated to understand and keeps the
interface clean enough so that we don't have to update every caller of
'fork_inferior' (which was a problem with previous designs I tried).

The subsequent patch will build on top of this one and implement the
errno-passing-via-pipe on the GNU/Linux target.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	 * nat/fork-inferior.c: Include "gdbsupport/scoped_pipe.h".
	 (default_trace_me_fail_reason): New function.
	 (trace_me_fail_reason): New variable.
	 (write_trace_errno_to_pipe): New function.
	 (read_trace_errno_from_pipe): Likewise.
	 (check_child_trace_me_errno): Likewise.
	 (traceme_info): New struct.
	 (fork_inferior_1): Renamed from 'fork_inferior'.
	 (fork_inferior): New overloads.
	 (trace_start_error_with_name): Add "append" parameter.
	 * nat/fork-inferior.h (fork_inferior): Expand comment.
	 Add overload declaration.
	 (trace_start_error_with_name): Add "append" parameter.
	 (trace_me_fail_reason): New variable.
	 (check_child_trace_me_errno): New function.
	 (write_trace_errno_to_pipe): Likewise.
---
 gdb/nat/fork-inferior.c | 231 ++++++++++++++++++++++++++++++++++++----
 gdb/nat/fork-inferior.h |  87 ++++++++++++---
 2 files changed, 288 insertions(+), 30 deletions(-)

diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 1185ef8998..223ff44195 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -27,6 +27,7 @@
 #include "gdbsupport/pathstuff.h"
 #include "gdbsupport/signals-state-save-restore.h"
 #include "gdbsupport/gdb_tilde_expand.h"
+#include "gdbsupport/scoped_pipe.h"
 #include <vector>
 
 extern char **environ;
@@ -262,16 +263,157 @@ execv_argv::init_for_shell (const char *exec_file,
   m_argv.push_back (NULL);
 }
 
-/* See nat/fork-inferior.h.  */
+/* Default implementation of 'trace_me_fail_reason'.  Always return
+   an empty string.  */
 
-pid_t
-fork_inferior (const char *exec_file_arg, const std::string &allargs,
-	       char **env, void (*traceme_fun) (),
-	       gdb::function_view<void (int)> init_trace_fun,
-	       void (*pre_trace_fun) (),
-	       const char *shell_file_arg,
-               void (*exec_fun)(const char *file, char * const *argv,
-                                char * const *env))
+static std::string
+default_trace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* See fork-inferior.h.  */
+
+std::string (*trace_me_fail_reason) (int err)
+  = default_trace_me_fail_reason;
+
+/* See fork-inferior.h.  */
+
+void
+write_trace_errno_to_pipe (int writepipe, int trace_errno)
+{
+  ssize_t writeret;
+
+  do
+    {
+      writeret = write (writepipe, &trace_errno, sizeof (trace_errno));
+    }
+  while (writeret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (writeret < 0)
+    error (_("Could not write trace errno: %s"), safe_strerror (errno));
+}
+
+/* Helper function to read TRACE_ERRNO from READPIPE, which handles
+   EINTR/EAGAIN and throws and exception if there was an error.  */
+
+static int
+read_trace_errno_from_pipe (int readpipe)
+{
+  ssize_t readret;
+  int trace_errno;
+
+  do
+    {
+      readret = read (readpipe, &trace_errno, sizeof (trace_errno));
+    }
+  while (readret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (readret < 0)
+    error (_("Could not read trace errno: %s"), safe_strerror (errno));
+
+  return trace_errno;
+}
+
+/* See fork-inferior.h.  */
+
+void
+check_child_trace_me_errno (int readpipe)
+{
+  fd_set rset;
+  struct timeval timeout;
+  int ret;
+
+  /* Make sure we have a valid 'trace_me_fail_reason' function
+     defined.  */
+  gdb_assert (trace_me_fail_reason != nullptr);
+
+  FD_ZERO (&rset);
+  FD_SET (readpipe, &rset);
+
+  /* Five seconds should be plenty of time to wait for the child's
+     reply.  */
+  timeout.tv_sec = 5;
+  timeout.tv_usec = 0;
+
+  do
+    {
+      ret = select (readpipe + 1, &rset, NULL, NULL, &timeout);
+    }
+  while (ret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (ret < 0)
+    perror_with_name ("select");
+  else if (ret == 0)
+    error (_("Timeout while waiting for child's trace errno"));
+  else
+    {
+      int child_errno;
+
+      child_errno = read_trace_errno_from_pipe (readpipe);
+
+      if (child_errno != 0)
+	{
+	  /* The child can't use TRACE_TRACEME.  We have to check whether
+	     we know the reason for the failure, and then error out.  */
+	  std::string reason = trace_me_fail_reason (child_errno);
+
+	  if (reason.empty ())
+	    reason = "Could not determine reason for trace failure.";
+
+	  /* The child is supposed to display a warning containing the
+	     safe_strerror message before us, so we just display the
+	     possible reason for the failure.  */
+	  error ("%s", reason.c_str ());
+	}
+    }
+}
+
+/* Helper struct for fork_inferior_1, containing information on
+   whether we should check if TRACEME_FUN was successfully called or
+   not.  */
+
+struct traceme_info
+{
+  /* True if we should check whether the call to 'traceme_fun
+     (TRACE_ME...)' succeeded or not. */
+  bool check_trace_me_fail_reason;
+
+  union
+  {
+    /* The non-check version of TRACEME_FUN.  It will be set if
+       CHECK_TRACEME_FAIL_REASON is false.
+
+       This function will usually just perform the call to whatever
+       trace function needed to start tracing the inferior (e.g.,
+       ptrace).  */
+    void (*traceme_fun_nocheck) ();
+
+    /* The check version of TRACEME_FUN.  It will be set if
+       CHECK_TRACEME_FAIL_REASON is true.
+
+       This function will usually perform the call to whatever trace
+       function needed to start tracing the inferior, but will also
+       write its errno value to TRACE_ERRNO_PIPE, so that
+       fork_inferior_1 can check whether it suceeded.  */
+    void (*traceme_fun_check) (int trace_errno_pipe);
+  } u;
+};
+
+/* Helper function.
+
+   Depending on the value of TRACEME_INFO.CHECK_TRACEME_FAIL_REASON,
+   this function will check whether the call to TRACEME_FUN succeeded
+   or not.  */
+
+static pid_t
+fork_inferior_1 (const char *exec_file_arg, const std::string &allargs,
+		 char **env, const struct traceme_info traceme_info,
+		 gdb::function_view<void (int)> init_trace_fun,
+		 void (*pre_trace_fun) (),
+		 const char *shell_file_arg,
+		 void (*exec_fun)(const char *file, char * const *argv,
+				  char * const *env))
 {
   pid_t pid;
   /* Set debug_fork then attach to the child while it sleeps, to debug.  */
@@ -283,6 +425,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   int save_errno;
   const char *inferior_cwd;
   std::string expanded_inferior_cwd;
+  scoped_pipe trace_pipe;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -365,12 +508,6 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
   if (pid == 0)
     {
-      /* Close all file descriptors except those that gdb inherited
-	 (usually 0/1/2), so they don't leak to the inferior.  Note
-	 that this closes the file descriptors of all secondary
-	 UIs.  */
-      close_most_fds ();
-
       /* Change to the requested working directory if the user
 	 requested it.  */
       if (inferior_cwd != NULL)
@@ -392,7 +529,10 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
          for the inferior.  */
 
       /* "Trace me, Dr. Memory!"  */
-      (*traceme_fun) ();
+      if (traceme_info.check_trace_me_fail_reason)
+	(*traceme_info.u.traceme_fun_check) (trace_pipe.get_write_end ());
+      else
+	(*traceme_info.u.traceme_fun_nocheck) ();
 
       /* The call above set this process (the "child") as debuggable
         by the original gdb process (the "parent").  Since processes
@@ -403,6 +543,12 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
         saying "not parent".  Sorry; you'll have to use print
         statements!  */
 
+      /* Close all file descriptors except those that gdb inherited
+	 (usually 0/1/2), so they don't leak to the inferior.  Note
+	 that this closes the file descriptors of all secondary
+	 UIs, and the trace errno pipe (if it's open).  */
+      close_most_fds ();
+
       restore_original_signals_state ();
 
       /* There is no execlpe call, so we have to set the environment
@@ -431,6 +577,13 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
       _exit (0177);
     }
 
+  if (traceme_info.check_trace_me_fail_reason)
+    {
+      /* Check the trace errno, and inform the user about the reason
+	 of the failure, if there was any.  */
+      check_child_trace_me_errno (trace_pipe.get_read_end ());
+    }
+
   /* Restore our environment in case a vforked child clob'd it.  */
   environ = save_our_env;
 
@@ -448,6 +601,48 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   return pid;
 }
 
+/* See fork-inferior.h.  */
+
+pid_t
+fork_inferior (const char *exec_file_arg, const std::string &allargs,
+	       char **env, void (*traceme_fun) (),
+	       gdb::function_view<void (int)> init_trace_fun,
+	       void (*pre_trace_fun) (),
+	       const char *shell_file_arg,
+               void (*exec_fun)(const char *file, char * const *argv,
+                                char * const *env))
+{
+  struct traceme_info traceme_info;
+
+  traceme_info.check_trace_me_fail_reason = false;
+  traceme_info.u.traceme_fun_nocheck = traceme_fun;
+
+  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
+			  init_trace_fun, pre_trace_fun, shell_file_arg,
+			  exec_fun);
+}
+
+/* See fork-inferior.h.  */
+
+pid_t
+fork_inferior (const char *exec_file_arg, const std::string &allargs,
+	       char **env, void (*traceme_fun) (int trace_errno_pipe),
+	       gdb::function_view<void (int)> init_trace_fun,
+	       void (*pre_trace_fun) (),
+	       const char *shell_file_arg,
+               void (*exec_fun)(const char *file, char * const *argv,
+                                char * const *env))
+{
+  struct traceme_info traceme_info;
+
+  traceme_info.check_trace_me_fail_reason = true;
+  traceme_info.u.traceme_fun_check = traceme_fun;
+
+  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
+			  init_trace_fun, pre_trace_fun, shell_file_arg,
+			  exec_fun);
+}
+
 /* See nat/fork-inferior.h.  */
 
 ptid_t
@@ -592,7 +787,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index cf6f137edd..b67215353f 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -32,17 +32,41 @@ struct process_stratum_target;
 #define START_INFERIOR_TRAPS_EXPECTED 1
 
 /* Start an inferior Unix child process and sets inferior_ptid to its
-   pid.  EXEC_FILE is the file to run.  ALLARGS is a string containing
-   the arguments to the program.  ENV is the environment vector to
-   pass.  SHELL_FILE is the shell file, or NULL if we should pick
-   one.  EXEC_FUN is the exec(2) function to use, or NULL for the default
-   one.  */
-
-/* This function is NOT reentrant.  Some of the variables have been
-   made static to ensure that they survive the vfork call.  */
+   pid.
+
+   EXEC_FILE is the file to run.
+
+   ALLARGS is a string containing the arguments to the program.
+
+   ENV is the environment vector to pass.
+
+   SHELL_FILE is the shell file, or NULL if we should pick one.
+
+   EXEC_FUN is the exec(2) function to use, or NULL for the default
+   one.
+
+   This function is NOT reentrant.  Some of the variables have been
+   made static to ensure that they survive the vfork call.
+
+   This function does not check whether the call to TRACEME_FUN
+   succeeded or not.  */
 extern pid_t fork_inferior (const char *exec_file_arg,
 			    const std::string &allargs,
-			    char **env, void (*traceme_fun) (),
+			    char **env,
+			    void (*traceme_fun) (),
+			    gdb::function_view<void (int)> init_trace_fun,
+			    void (*pre_trace_fun) (),
+			    const char *shell_file_arg,
+			    void (*exec_fun) (const char *file,
+					      char * const *argv,
+					      char * const *env));
+
+/* Like fork_inferior above, but check whether the call to TRACEME_FUN
+   succeeded or not.  */
+extern pid_t fork_inferior (const char *exec_file_arg,
+			    const std::string &allargs,
+			    char **env,
+			    void (*traceme_fun) (int trace_errno_pipe),
 			    gdb::function_view<void (int)> init_trace_fun,
 			    void (*pre_trace_fun) (),
 			    const char *shell_file_arg,
@@ -82,9 +106,48 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
+/* Pointer to function which can be called by
+   'check_child_trace_me_errno' when we need to determine the reason
+   of a e.g. 'ptrace (PTRACE_ME, ...)' failure.  ERR is the ERRNO
+   value set by the failing ptrace call.
+
+   By default, the function returns an empty string (see
+   fork-inferior.c).
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when trace fails (see linux-nat.c or
+   gdbserver/linux-low.c, for example).  */
+extern std::string (*trace_me_fail_reason) (int err);
+
+/* Check the "trace me" errno (generated when executing e.g. 'ptrace
+   (PTRACE_ME, ...)') of the child process that was created by
+   GDB/GDBserver when creating an inferior.  The errno value will be
+   passed via a pipe (see 'fork_inferior'), and READPIPE is the read
+   end of the pipe.
+
+   If possible (i.e., if 'trace_me_fail_reason' is defined by the
+   target), then we also try to determine the possible reason for a
+   failure.
+
+   The idea is to wait a few seconds (via 'select') until something is
+   written on READPIPE.  When that happens, we check if the child's
+   trace errno is different than 0.  If it is, we call the function
+   'trace_me_fail_reason' and try to obtain the reason for the
+   failure, and then throw an exception (with the reason as the
+   exception's message).
+
+   If nothing is written on the pipe, or if 'select' fails, we also
+   throw exceptions.  */
+extern void check_child_trace_me_errno (int readpipe);
+
+/* Helper function to write TRACE_ERRNO to WRITEPIPE, which handles
+   EINTR/EAGAIN and throws an exception if there was an error.  */
+extern void write_trace_errno_to_pipe (int writepipe, int trace_errno);
+
 #endif /* NAT_FORK_INFERIOR_H */
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 6/6] Fix comment for 'gdb_dlopen'
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
  2020-02-26 20:06     ` [PATCH 4/6] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
  2020-02-26 20:06     ` [PATCH 1/6] Introduce scoped_pipe.h Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
  2020-02-26 20:23       ` Christian Biesinger via gdb-patches
  2020-02-28 15:21       ` Tom Tromey
  2020-02-26 20:06     ` [PATCH 5/6] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
                       ` (4 subsequent siblings)
  7 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

The 'gdb_dlopen' function doesn't return NULL if the shlib load
fails;n it actually throws an error.  This patch updates the comment
to reflect this.

gdbsupport/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb-dlfcn.h (gdb_dlopen): Update comment.
---
 gdbsupport/gdb-dlfcn.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/gdbsupport/gdb-dlfcn.h b/gdbsupport/gdb-dlfcn.h
index 258cfebbc3..9e72a53dc0 100644
--- a/gdbsupport/gdb-dlfcn.h
+++ b/gdbsupport/gdb-dlfcn.h
@@ -32,8 +32,8 @@ struct dlclose_deleter
 typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
 
 /* Load the dynamic library file named FILENAME, and return a handle
-   for that dynamic library.  Return NULL if the loading fails for any
-   reason.  */
+   for that dynamic library.  Throw an error if the loading fails for
+   any reason.  */
 
 gdb_dlhandle_up gdb_dlopen (const char *filename);
 
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 4/6] Extend GNU/Linux to check for ptrace error
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
  2020-02-26 20:06     ` [PATCH 1/6] Introduce scoped_pipe.h Sergio Durigan Junior
                       ` (6 subsequent siblings)
  7 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

This patch implements the ptrace-errno-checking on the GNU/Linux
target (both native and remote).  It builds on top of the previous
'fork_inferior' extension patch.

The idea is to provide a new 'traceme_fun' for each ptrace backend,
which will accept a new integer argument representing the write end of
the ptrace status pipe (that was created in 'fork_inferior').  This
function will invoke the actual tracing syscall (which is 'ptrace' in
this case), get its errno value and write it back via the pipe.  You
can see examples of this new approach by looking at
'inf_ptrace_me' (GDB) or 'linux_ptrace_fun' (gdbserver).

The rest of the patch implements the necessary machinery to do
something useful with the errno information that we received from
'ptrace'.

In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_TRACEME will
fail with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

There is now a new function, 'linux_ptrace_restricted_fail_reason',
which does a few things to check what's wrong with ptrace:

  - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
    option is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /usr/bin/true
  ...
  (gdb) run
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Permission denied

  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) must
  be disabled in the target system (e.g., where GDBserver is running).

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /usr/bin/true
  ...
  (gdb) run
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Operation not permitted

  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  If you are debugging the inferior remotely, the ptrace restriction(s) must
  be disabled in the target system (e.g., where GDBserver is running).

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Permission denied
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2766868 No such process
  gdbserver: Could not trace the inferior process.
  gdbserver: ptrace: Permission denied

  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  Exiting.
  #

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.
  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).

This means that the series expands our documentation (in the next
patch) and creates a new appendix section named "Linux kernel ptrace
restrictions", with sub-sections for each possible restriction that
might be in place.

Notice how, on every message, we instruct the user to "do the right
thing" if gdbserver is being used.  This is because if the user
started gdbserver *before* any ptrace restriction was in place, and
then, for some reason, one or more restrictions get enabled, then the
error message will be displayed both on gdbserver *and* on the
connected GDB.  Since the user will be piloting GDB, it's important to
explicitly say that the ptrace restrictions are enabled in the target,
where gdbserver is running.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* inf-ptrace.c: Include "nat/fork-inferior.h".
	(inf_ptrace_me): New parameter "trace_errno_pipe".  Check
	"ptrace" errno.
	* linux-nat.c: Include "nat/fork-inferior.h".
	(attach_proc_task_lwp_callback): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
	(linux_nat_target::attach): Save "ERRNO".  Pass it to
	"linux_ptrace_attach_fail_reason".
	(_initialize_linux_nat): Set "trace_me_fail_reason".
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
	"nat/fork-inferior.h" and "gdbsupport/filestuff.h".
	(selinux_ftype): New type.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_1): New function, renamed
	from "linux_ptrace_attach_fail_reason".
	(linux_ptrace_attach_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_lwp): Likewise.
	(linux_ptrace_me_fail_reason): Likewise.
	(errno_pipe): New variable.
	(linux_child_function): Check "ptrace" errno.  Send it through
	the pipe.
	(linux_check_ptrace_features): Initialize pipe.  Check
	"ptrace" errno sent through the pipe.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): New
	function.
	(linux_ptrace_attach_fail_reason_lwp): Likewise.
	(linux_ptrace_me_fail_reason): Likewise.
	* remote.c (extended_remote_target::attach): Check error
	message on PACKET_ERROR.
	(remote_target::extended_remote_run): Likewise.

gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* linux-low.cc (linux_ptrace_fun): New parameter
	"trace_errno_wpipe".  Check "ptrace" errno.
	(attach_proc_task_lwp_callback): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
	(linux_process_target::attach): Likewise.
	(initialize_low): Set "trace_me_fail_reason".
	* server.cc (handle_v_attach): Check if "attach_inferior"
	succeeded.
	(handle_v_run): Likewise.
	* thread-db.cc (attach_thread): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
---
 gdb/inf-ptrace.c        |  18 +++-
 gdb/linux-nat.c         |  10 ++-
 gdb/nat/fork-inferior.c |   6 +-
 gdb/nat/fork-inferior.h |   2 +-
 gdb/nat/linux-ptrace.c  | 178 ++++++++++++++++++++++++++++++++++++++--
 gdb/nat/linux-ptrace.h  |  27 ++++--
 gdb/remote.c            |  40 ++++++++-
 gdbserver/linux-low.cc  |  31 +++++--
 gdbserver/server.cc     |  38 ++++++++-
 gdbserver/thread-db.cc  |   2 +-
 10 files changed, 318 insertions(+), 34 deletions(-)

diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index db17a76d94..8fb7264700 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -34,6 +34,7 @@
 #include "nat/fork-inferior.h"
 #include "utils.h"
 #include "gdbarch.h"
+#include "nat/fork-inferior.h"
 
 \f
 
@@ -97,10 +98,23 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 /* Prepare to be traced.  */
 
 static void
-inf_ptrace_me (void)
+inf_ptrace_me (int trace_errno_wpipe)
 {
   /* "Trace me, Dr. Memory!"  */
-  if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
+  int ret = ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  try
+    {
+      write_trace_errno_to_pipe (trace_errno_wpipe, ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  if (ret < 0)
     trace_start_error_with_name ("ptrace");
 }
 
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 81af83c4ac..31a5da8b52 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -31,6 +31,7 @@
 #include "nat/linux-ptrace.h"
 #include "nat/linux-procfs.h"
 #include "nat/linux-personality.h"
+#include "nat/fork-inferior.h"
 #include "linux-fork.h"
 #include "gdbthread.h"
 #include "gdbcmd.h"
@@ -1136,7 +1137,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
@@ -1191,8 +1192,9 @@ linux_nat_target::attach (const char *args, int from_tty)
     }
   catch (const gdb_exception_error &ex)
     {
+      int saved_errno = errno;
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
 
       if (!reason.empty ())
 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
@@ -4582,6 +4584,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  trace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 223ff44195..eb4c4625d7 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -394,9 +394,9 @@ struct traceme_info
 
        This function will usually perform the call to whatever trace
        function needed to start tracing the inferior, but will also
-       write its errno value to TRACE_ERRNO_PIPE, so that
+       write its errno value to TRACE_ERRNO_WPIPE, so that
        fork_inferior_1 can check whether it suceeded.  */
-    void (*traceme_fun_check) (int trace_errno_pipe);
+    void (*traceme_fun_check) (int trace_errno_wpipe);
   } u;
 };
 
@@ -626,7 +626,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
 pid_t
 fork_inferior (const char *exec_file_arg, const std::string &allargs,
-	       char **env, void (*traceme_fun) (int trace_errno_pipe),
+	       char **env, void (*traceme_fun) (int trace_errno_wpipe),
 	       gdb::function_view<void (int)> init_trace_fun,
 	       void (*pre_trace_fun) (),
 	       const char *shell_file_arg,
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index b67215353f..3fbead2e33 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -66,7 +66,7 @@ extern pid_t fork_inferior (const char *exec_file_arg,
 extern pid_t fork_inferior (const char *exec_file_arg,
 			    const std::string &allargs,
 			    char **env,
-			    void (*traceme_fun) (int trace_errno_pipe),
+			    void (*traceme_fun) (int trace_errno_wpipe),
 			    gdb::function_view<void (int)> init_trace_fun,
 			    void (*pre_trace_fun) (),
 			    const char *shell_file_arg,
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 5335d69092..b3fcf8bc07 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,9 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/filestuff.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -30,11 +33,93 @@
    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 *);
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid)
+/* 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;
+
+  try
+    {
+      handle = gdb_dlopen ("libselinux.so.1");
+    }
+  catch (const gdb_exception_error &e)
+    {
+    }
+
+  if (handle != nullptr)
+    {
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  setsebool deny_ptrace off\n"));
+    }
+
+  gdb_file_up yama_ptrace_scope
+    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
+
+  if (yama_ptrace_scope != nullptr)
+    {
+      char yama_scope = fgetc (yama_ptrace_scope.get ());
+
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
+    }
+
+  if (ret.empty ())
+    {
+      /* It wasn't possible to determine the exact reason for the
+	 ptrace error.  Let's just emit a generic error message
+	 pointing the user to our documentation, where she can find
+	 instructions on how to try to diagnose the problem.  */
+      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.");
+    }
+
+  /* The user may be debugging remotely, so we have to warn that
+     the instructions above should be performed in the target.  */
+  string_appendf (ret,
+		  _("\n\
+If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
+be disabled in the target system (e.g., where GDBserver is running)."));
+
+  return ret;
+}
+
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
+
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -56,10 +141,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+
+  if (!ptrace_restrict.empty ())
+    result += "\n" + ptrace_restrict;
+
+  return result;
+}
+
+/* See linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
@@ -68,6 +167,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.  */
@@ -257,6 +364,12 @@ linux_ptrace_test_ret_to_nx (void)
 #endif /* defined __i386__ || defined __x86_64__ */
 }
 
+/* If the PTRACE_TRACEME call on linux_child_function errors, we need
+   to be able to send ERRNO back to the parent so that it can check
+   whether there are restrictions in place preventing ptrace from
+   working.  We do that with a pipe.  */
+static int errno_pipe[2];
+
 /* Helper function to fork a process and make the child process call
    the function FUNCTION, passing CHILD_STACK as parameter.
 
@@ -321,7 +434,30 @@ 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);
+  /* Close read end.  */
+  close (errno_pipe[0]);
+
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  /* Write ERRNO to the pipe, even if it's zero, and close the writing
+     end of the pipe.  */
+  try
+    {
+      write_trace_errno_to_pipe (errno_pipe[1], ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  close (errno_pipe[1]);
+
+  if (ret != 0)
+    trace_start_error_with_name ("ptrace");
+
   kill (getpid (), SIGSTOP);
 
   /* Fork a grandchild.  */
@@ -346,12 +482,40 @@ linux_check_ptrace_features (void)
   /* Initialize the options.  */
   supported_ptrace_options = 0;
 
+  /* Initialize our pipe.  */
+  if (gdb_pipe_cloexec (errno_pipe) < 0)
+    perror_with_name ("gdb_pipe_cloexec");
+
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
      eventually fork a grandchild so we can test fork event
      reporting.  */
   child_pid = linux_fork_to_function (NULL, linux_child_function);
 
+  /* We don't need the write end of the pipe anymore.  */
+  close (errno_pipe[1]);
+
+  try
+    {
+      /* Check whether 'ptrace (PTRACE_ME, ...)' failed when being
+	 invoked by the child.  If it did, we might get the
+	 possible reason for it as the exception message.  */
+      check_child_trace_me_errno (errno_pipe[0]);
+    }
+  catch (const gdb_exception &e)
+    {
+      /* Close the pipe so we don't leak fd's.  */
+      close (errno_pipe[0]);
+
+      /* A failure here means that PTRACE_ME failed, which means that
+	 GDB/gdbserver will most probably not work correctly.  If we
+	 want to be pedantic, we could just call 'exit' here.
+	 However, let's just re-throw the exception.  */
+      throw;
+    }
+
+  close (errno_pipe[0]);
+
   ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name (("waitpid"));
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 65568301f2..7cb77114ca 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,12 +176,27 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
-extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
-
-/* 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);
+/* 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);
+
+/* Find all possible reasons we could have failed to attach to PID's
+   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
+   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
+   this function should be used when attaching to an LWP other than
+   the leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
diff --git a/gdb/remote.c b/gdb/remote.c
index 4a70ab3fb0..7e0655974c 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5882,9 +5882,26 @@ extended_remote_target::attach (const char *args, int from_tty)
       break;
     case PACKET_UNKNOWN:
       error (_("This target does not support attaching to a process"));
+    case PACKET_ERROR:
+      {
+	std::string errmsg = rs->buf.data ();
+
+	/* Check if we have a specific error (i.e., not a generic
+	   "E01") coming from the target.  If there is, we print it
+	   here.  */
+	if (startswith (errmsg.c_str (), "E."))
+	  {
+	    /* Get rid of the "E." prefix.  */
+	    errmsg.erase (0, 2);
+	  }
+
+	error (_("Attaching to %s failed%s%s"),
+	       target_pid_to_str (ptid_t (pid)).c_str (),
+	       !errmsg.empty () ? "\n" : "",
+	       errmsg.c_str ());
+      }
     default:
-      error (_("Attaching to %s failed"),
-	     target_pid_to_str (ptid_t (pid)).c_str ());
+      gdb_assert_not_reached (_("bad switch"));
     }
 
   set_current_inferior (remote_add_inferior (false, pid, 1, 0));
@@ -10003,8 +10020,23 @@ remote_target::extended_remote_run (const std::string &args)
 	error (_("Running the default executable on the remote target failed; "
 		 "try \"set remote exec-file\"?"));
       else
-	error (_("Running \"%s\" on the remote target failed"),
-	       remote_exec_file);
+	{
+	  std::string errmsg = rs->buf.data ();
+
+	  /* Check if we have a specific error (i.e., not a generic
+	     "E01") coming from the target.  If there is, we print it
+	     here.  */
+	  if (startswith (errmsg.c_str (), "E."))
+	    {
+	      /* Get rid of the "E." prefix.  */
+	      errmsg.erase (0, 2);
+	    }
+
+	  error (_("Running \"%s\" on the remote target failed%s%s"),
+		 remote_exec_file,
+		 !errmsg.empty () ? "\n" : "",
+		 errmsg.c_str ());
+	}
     default:
       gdb_assert_not_reached (_("bad switch"));
     }
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 2872bc78da..42283802dd 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -968,10 +968,24 @@ add_lwp (ptid_t ptid)
    actually initiating the tracing of the inferior.  */
 
 static void
-linux_ptrace_fun ()
+linux_ptrace_fun (int ptrace_errno_wpipe)
 {
-  if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
-	      (PTRACE_TYPE_ARG4) 0) < 0)
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  try
+    {
+      write_trace_errno_to_pipe (ptrace_errno_wpipe, ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  errno = ptrace_errno;
+  if (ret < 0)
     trace_start_error_with_name ("ptrace");
 
   if (setpgid (0, 0) < 0)
@@ -1170,7 +1184,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1202,8 +1216,8 @@ linux_process_target::attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
-      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
+      error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
     }
 
   /* Don't ignore the initial SIGSTOP if we just attached to this
@@ -7552,5 +7566,10 @@ initialize_low (void)
 
   initialize_low_arch ();
 
+  /* Initialize the 'trace_me_fail_reason' function pointer.  We will
+     use this to determine the reason for possible failures when
+     invoking 'ptrace (PTRACE_ME, ...)'.  */
+  trace_me_fail_reason = linux_ptrace_me_fail_reason;
+
   linux_check_ptrace_features ();
 }
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index a4cb1eb418..d0b0c5a4ad 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2891,9 +2891,31 @@ handle_v_attach (char *own_buf)
 {
   client_state &cs = get_client_state ();
   int pid;
+  int ret;
 
   pid = strtol (own_buf + 8, NULL, 16);
-  if (pid != 0 && attach_inferior (pid) == 0)
+
+  if (pid <= 0)
+    {
+      write_enn (own_buf);
+      return 0;
+    }
+
+  try
+    {
+      /* Attach to the specified PID.  This function can throw, so we
+	 make sure to catch the exception and send it (as an error
+	 packet) back to GDB.  */
+      ret = attach_inferior (pid);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      fprintf (stderr, "%s\n", e.what ());
+      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
+      return 0;
+    }
+
+  if (ret == 0)
     {
       /* Don't report shared library events after attaching, even if
 	 some libraries are preloaded.  GDB will always poll the
@@ -3029,7 +3051,19 @@ handle_v_run (char *own_buf)
   free_vector_argv (program_args);
   program_args = new_argv;
 
-  target_create_inferior (program_path.get (), program_args);
+  try
+    {
+      /* Create the inferior.  This function can throw, so we make
+	 sure to catch the exception and send it (as an error packet)
+	 back to GDB.  */
+      target_create_inferior (program_path.get (), program_args);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      fprintf (stderr, "%s\n", e.what ());
+      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
+      return 0;
+    }
 
   if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
     {
diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc
index 2bb6d28820..60ceb7b663 100644
--- a/gdbserver/thread-db.cc
+++ b/gdbserver/thread-db.cc
@@ -224,7 +224,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 0/6] Improve ptrace-error detection
  2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
  2019-09-26 17:32   ` Tom Tromey
  2019-09-26 18:13   ` Pedro Alves
@ 2020-02-26 20:06   ` Sergio Durigan Junior
  2020-02-26 20:06     ` [PATCH 4/6] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
                       ` (7 more replies)
  2 siblings, 8 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

This patch series is the continuation of:

  [PATCH v5] Improve ptrace-error detection on Linux targets
  https://sourceware.org/ml/gdb-patches/2019-09/msg00504.html

I decided to start a new series because this involved a rewrite of
several parts of the patch.  I addressed all of the comments I
received back in September (mostly from Pedro), but I also did some
improvements, especially in the 'fork_inferior' code.

After submitting this series to our Buildbot, no regressions were
found.

Sergio Durigan Junior (6):
  Introduce scoped_pipe.h
  Don't reset errno/bfd_error on 'throw_perror_with_name'
  Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  Extend GNU/Linux to check for ptrace error
  Document Linux-specific possible ptrace restrictions
  Fix comment for 'gdb_dlopen'

 gdb/Makefile.in                       |   1 +
 gdb/doc/gdb.texinfo                   | 143 ++++++++++++++++
 gdb/inf-ptrace.c                      |  18 +-
 gdb/linux-nat.c                       |  10 +-
 gdb/nat/fork-inferior.c               | 231 ++++++++++++++++++++++++--
 gdb/nat/fork-inferior.h               |  87 ++++++++--
 gdb/nat/linux-ptrace.c                | 178 +++++++++++++++++++-
 gdb/nat/linux-ptrace.h                |  27 ++-
 gdb/remote.c                          |  40 ++++-
 gdb/unittests/scoped_pipe-selftests.c |  96 +++++++++++
 gdb/utils.c                           |   6 -
 gdbserver/linux-low.cc                |  31 +++-
 gdbserver/server.cc                   |  38 ++++-
 gdbserver/thread-db.cc                |   2 +-
 gdbserver/utils.cc                    |   2 +
 gdbsupport/gdb-dlfcn.h                |   4 +-
 gdbsupport/scoped_pipe.h              |  63 +++++++
 17 files changed, 909 insertions(+), 68 deletions(-)
 create mode 100644 gdb/unittests/scoped_pipe-selftests.c
 create mode 100644 gdbsupport/scoped_pipe.h

-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
  2020-02-26 20:06     ` [PATCH 4/6] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
  2020-02-28 15:23       ` Tom Tromey
  2020-02-28 19:20       ` Pedro Alves
  2020-02-26 20:06     ` [PATCH 6/6] Fix comment for 'gdb_dlopen' Sergio Durigan Junior
                       ` (5 subsequent siblings)
  7 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

This simple patch introduces gdbsupport/scoped_pipe.h, which is based
on gdbsupport/scoped_fd.h.  When the object is instantiated, a pipe is
created using 'gdb_pipe_cloexec'.  There are two methods (get_read_end
and get_write_end) that allow the user to obtain the read/write ends
of the pipe (no more messing with [0] and [1]), and when the object is
destroyed, the pipe is closed (both ends).

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* unittests/scoped_pipe-selftests.c: New file.

gdbsupport/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* scoped_pipe.h: New file.
---
 gdb/Makefile.in                       |  1 +
 gdb/unittests/scoped_pipe-selftests.c | 96 +++++++++++++++++++++++++++
 gdbsupport/scoped_pipe.h              | 63 ++++++++++++++++++
 3 files changed, 160 insertions(+)
 create mode 100644 gdb/unittests/scoped_pipe-selftests.c
 create mode 100644 gdbsupport/scoped_pipe.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index f9606b8fc7..d5f1450035 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -444,6 +444,7 @@ SUBDIR_UNITTESTS_SRCS = \
 	unittests/rsp-low-selftests.c \
 	unittests/scoped_fd-selftests.c \
 	unittests/scoped_mmap-selftests.c \
+	unittests/scoped_pipe-selftests.c \
 	unittests/scoped_restore-selftests.c \
 	unittests/string_view-selftests.c \
 	unittests/style-selftests.c \
diff --git a/gdb/unittests/scoped_pipe-selftests.c b/gdb/unittests/scoped_pipe-selftests.c
new file mode 100644
index 0000000000..dd634bec97
--- /dev/null
+++ b/gdb/unittests/scoped_pipe-selftests.c
@@ -0,0 +1,96 @@
+/* Self tests for scoped_pipe for GDB, the GNU debugger.
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "gdbsupport/scoped_pipe.h"
+#include "config.h"
+#include "gdbsupport/selftest.h"
+
+namespace selftests {
+namespace scoped_pipe {
+
+/* Test that the pipe is correctly created.  */
+
+static void
+test_create ()
+{
+  ::scoped_pipe spipe;
+
+  SELF_CHECK (spipe.get_read_end () > 0);
+  SELF_CHECK (spipe.get_write_end () > 0);
+}
+
+/* Test that we can write and read from the pipe.  */
+
+static void
+test_transmission ()
+{
+  int foo = 123;
+  ::scoped_pipe spipe;
+
+  /* Write to the pipe.  */
+  {
+    ssize_t writeret;
+
+    do
+      {
+	writeret = write (spipe.get_write_end (), &foo, sizeof (foo));
+      }
+    while (writeret < 0 && (errno == EAGAIN || errno == EINTR));
+
+    SELF_CHECK (writeret > 0);
+  }
+
+  /* Read from the pipe, and check if the value read is the same as
+     the one that was written.  */
+  {
+    ssize_t readret;
+    int read_foo;
+
+    do
+      {
+	readret = read (spipe.get_read_end (), &read_foo, sizeof (read_foo));
+      }
+    while (readret < 0 && (errno == EAGAIN || errno == EINTR));
+
+    SELF_CHECK (readret > 0);
+
+    SELF_CHECK (read_foo == foo);
+  }
+}
+
+/* Run selftests.  */
+static void
+run_tests ()
+{
+  test_create ();
+  test_transmission ();
+}
+
+} /* namespace scoped_pipe */
+} /* namespace selftests */
+
+void _initialize_scoped_pipe_selftests ();
+void
+_initialize_scoped_pipe_selftests ()
+{
+  selftests::register_test ("scoped_pipe",
+			    selftests::scoped_pipe::run_tests);
+}
diff --git a/gdbsupport/scoped_pipe.h b/gdbsupport/scoped_pipe.h
new file mode 100644
index 0000000000..8f133b442f
--- /dev/null
+++ b/gdbsupport/scoped_pipe.h
@@ -0,0 +1,63 @@
+/* scoped_pipe, automatically close a pipe.
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef COMMON_SCOPED_PIPE_H
+#define COMMON_SCOPED_PIPE_H
+
+#include <unistd.h>
+#include "filestuff.h"
+
+/* A smart-pointer-like class to automatically close a pipe.  */
+
+class scoped_pipe
+{
+public:
+  explicit scoped_pipe ()
+  {
+    if (gdb_pipe_cloexec (m_pipe) < 0)
+      error (_("gdb_pipe_cloexec: %s"), safe_strerror (errno));
+  }
+
+  ~scoped_pipe ()
+  {
+    if (m_pipe[0] >= 0)
+      close (m_pipe[0]);
+    if (m_pipe[1] >= 0)
+      close (m_pipe[1]);
+  }
+
+  DISABLE_COPY_AND_ASSIGN (scoped_pipe);
+
+  /* Get the read end of the pipe.  */
+  int get_read_end () const noexcept
+  {
+    return m_pipe[0];
+  }
+
+  /* Get the write end of the pipe.  */
+  int get_write_end () const noexcept
+  {
+    return m_pipe[1];
+  }
+
+private:
+  int m_pipe[2];
+};
+
+#endif /* ! COMMON_SCOPED_PIPE_H */
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 5/6] Document Linux-specific possible ptrace restrictions
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
                       ` (2 preceding siblings ...)
  2020-02-26 20:06     ` [PATCH 6/6] Fix comment for 'gdb_dlopen' Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
  2020-02-26 21:00       ` Ruslan Kabatsayev
  2020-02-26 20:06     ` [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
                       ` (3 subsequent siblings)
  7 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

This patch creates a new "Linux kernel ptrace restrictions" which
documents possible causes that can be prevent the inferior from being
correctly started/debugged.

This has been pre-approved by Eli.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.
---
 gdb/doc/gdb.texinfo | 143 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 143 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index f1798e35b5..a95158d5d3 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel ptrace restrictions::        Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -45629,6 +45632,146 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} and
+@code{gdbserver} on GNU/Linux to, among other things, attach to a new
+or existing inferior in order to start debugging it.  Due to security
+concerns, some distributions and vendors disable or severely restrict
+the ability to perform these operations, which can make @value{GDBN}
+or @code{gdbserver} malfunction.  In this section, we will expand on
+how this malfunction can manifest itself, and how to modify the
+system's settings in order to be able to use @value{GDBN} and
+@code{gdbserver} properly.
+
+@menu
+* The error message::                   The error message displayed when the
+                                        system prevents @value{GDBN}
+                                        or @code{gdbserver} from using
+                                        @code{ptrace}
+* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
+* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
+* Docker and seccomp::                  Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The error message
+@appendixsection The error message
+
+When the system prevents @value{GDBN} or @code{gdbserver} from using
+the @code{ptrace} system call, you will likely see a descriptive error
+message explaining what is wrong and how to attempt to fix the
+problem.  For example, when SELinux's @code{deny_ptrace} option is
+enabled, you can see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing @value{GDBN}
+from using 'ptrace'.  You can disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+If you are debugging the inferior remotely, the instruction(s) above must
+be performed in the target system (e.g., where GDBserver is running).
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} or
+@code{gdbserver} can be affected by it.  You can temporarily disable
+the feature by doing, as root:
+
+@smallexample
+# sysctl kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+You can make this permanent by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN} or @code{gdbserver}.  To do that,
+you can use the options @code{--cap-add=SYS_PTRACE --security-opt
+seccomp=unconfined} when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
                       ` (3 preceding siblings ...)
  2020-02-26 20:06     ` [PATCH 5/6] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
@ 2020-02-26 20:06     ` Sergio Durigan Junior
  2020-02-28 15:29       ` Tom Tromey
  2020-02-28 19:49       ` Pedro Alves
  2020-02-26 20:06     ` [PATCH 3/6] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
                       ` (2 subsequent siblings)
  7 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:06 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev,
	Sergio Durigan Junior

Since this hunk may be a bit controversial, I decided to split it into
a separate patch.  This is going to be needed by the ptrace-error
feature; GDB will need to be able to access the value of errno even
after a call to our 'perror'-like functions.

The idea here is that we actually *want* errno to propagate between
our customized 'perror' calls.  We currently have this code on
'throw_perror_with_name':

  /* I understand setting these is a matter of taste.  Still, some people
     may clear errno but not know about bfd_error.  Doing this here is not
     unreasonable.  */
  bfd_set_error (bfd_error_no_error);
  errno = 0;

git blame tells me that this piece of code is pretty old; the commit
that "introduced" it is:

  commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc
  Author: Stan Shebs <shebs@codesourcery.com>
  Date:   Fri Apr 16 01:35:26 1999 +0000

      Initial creation of sourceware repository

so yeah...

If we go to the POSIX specification for 'perror', it doesn't really
say anything about whether errno should be preserved or not.  It does,
however, say that 'perror's messages should be the same as those
returned by 'strerror', and 'strerror' is not supposed to alter errno
if the call is successful.

Maybe when our wrapper was written it was OK to modify errno, I don't
know.  But I'd like to propose that we stick to POSIX in this case.

Another small hunk is the one that saves/restores errno on gdbserver's
'perror_with_name', but this one is pretty trivial, I think.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* utils.c (throw_perror_with_name): Don't reset
	errno/bfd_error.

gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* utils.cc (perror_with_name): Save/restore errno.
---
 gdb/utils.c        | 6 ------
 gdbserver/utils.cc | 2 ++
 2 files changed, 2 insertions(+), 6 deletions(-)

diff --git a/gdb/utils.c b/gdb/utils.c
index 0b470120a2..df8add1afa 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -595,12 +595,6 @@ throw_perror_with_name (enum errors errcode, const char *string)
 {
   std::string combined = perror_string (string);
 
-  /* I understand setting these is a matter of taste.  Still, some people
-     may clear errno but not know about bfd_error.  Doing this here is not
-     unreasonable.  */
-  bfd_set_error (bfd_error_no_error);
-  errno = 0;
-
   throw_error (errcode, _("%s."), combined.c_str ());
 }
 
diff --git a/gdbserver/utils.cc b/gdbserver/utils.cc
index d88f4ac5ca..50ebba43ca 100644
--- a/gdbserver/utils.cc
+++ b/gdbserver/utils.cc
@@ -46,6 +46,7 @@ perror_with_name (const char *string)
 {
   const char *err;
   char *combined;
+  int saved_errno = errno;
 
   err = safe_strerror (errno);
   if (err == NULL)
@@ -56,6 +57,7 @@ perror_with_name (const char *string)
   strcat (combined, ": ");
   strcat (combined, err);
 
+  errno = saved_errno;
   error ("%s.", combined);
 }
 
-- 
2.24.1

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 6/6] Fix comment for 'gdb_dlopen'
  2020-02-26 20:06     ` [PATCH 6/6] Fix comment for 'gdb_dlopen' Sergio Durigan Junior
@ 2020-02-26 20:23       ` Christian Biesinger via gdb-patches
  2020-02-26 20:49         ` Sergio Durigan Junior
  2020-02-28 15:21       ` Tom Tromey
  1 sibling, 1 reply; 98+ messages in thread
From: Christian Biesinger via gdb-patches @ 2020-02-26 20:23 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On Wed, Feb 26, 2020 at 2:06 PM Sergio Durigan Junior
<sergiodj@redhat.com> wrote:
>
> The 'gdb_dlopen' function doesn't return NULL if the shlib load
> fails;n it actually throws an error.  This patch updates the comment

Typo here (;n)

> to reflect this.
>
> gdbsupport/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
>         * gdb-dlfcn.h (gdb_dlopen): Update comment.
> ---
>  gdbsupport/gdb-dlfcn.h | 4 ++--
>  1 file changed, 2 insertions(+), 2 deletions(-)
>
> diff --git a/gdbsupport/gdb-dlfcn.h b/gdbsupport/gdb-dlfcn.h
> index 258cfebbc3..9e72a53dc0 100644
> --- a/gdbsupport/gdb-dlfcn.h
> +++ b/gdbsupport/gdb-dlfcn.h
> @@ -32,8 +32,8 @@ struct dlclose_deleter
>  typedef std::unique_ptr<void, dlclose_deleter> gdb_dlhandle_up;
>
>  /* Load the dynamic library file named FILENAME, and return a handle
> -   for that dynamic library.  Return NULL if the loading fails for any
> -   reason.  */
> +   for that dynamic library.  Throw an error if the loading fails for
> +   any reason.  */
>
>  gdb_dlhandle_up gdb_dlopen (const char *filename);
>
> --
> 2.24.1
>

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 6/6] Fix comment for 'gdb_dlopen'
  2020-02-26 20:23       ` Christian Biesinger via gdb-patches
@ 2020-02-26 20:49         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 20:49 UTC (permalink / raw)
  To: Christian Biesinger
  Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On Wednesday, February 26 2020, Christian Biesinger wrote:

> On Wed, Feb 26, 2020 at 2:06 PM Sergio Durigan Junior
> <sergiodj@redhat.com> wrote:
>>
>> The 'gdb_dlopen' function doesn't return NULL if the shlib load
>> fails;n it actually throws an error.  This patch updates the comment
>
> Typo here (;n)

Thanks!  I fixed this locally.  After I submitted the series, I thought
that this one should probably have been merged with 5/6.  Oh, well...

Cheers,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 5/6] Document Linux-specific possible ptrace restrictions
  2020-02-26 20:06     ` [PATCH 5/6] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
@ 2020-02-26 21:00       ` Ruslan Kabatsayev
  2020-02-26 22:08         ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Ruslan Kabatsayev @ 2020-02-26 21:00 UTC (permalink / raw)
  To: Sergio Durigan Junior; +Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii

On Wed, 26 Feb 2020 at 23:06, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
>
> This patch creates a new "Linux kernel ptrace restrictions" which
> documents possible causes that can be prevent the inferior from being
> correctly started/debugged.
>
> This has been pre-approved by Eli.
>
> gdb/doc/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
>         * gdb.texinfo (Linux kernel ptrace restrictions): New appendix
>         section.
> ---
>  gdb/doc/gdb.texinfo | 143 ++++++++++++++++++++++++++++++++++++++++++++
>  1 file changed, 143 insertions(+)
>
> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
> index f1798e35b5..a95158d5d3 100644
> --- a/gdb/doc/gdb.texinfo
> +++ b/gdb/doc/gdb.texinfo
> @@ -182,6 +182,9 @@ software in general.  We will miss him.
>                                  @value{GDBN}
>  * Operating System Information:: Getting additional information from
>                                   the operating system
> +* Linux kernel ptrace restrictions::        Restrictions sometimes
> +                                            imposed by the Linux
> +                                            kernel on @code{ptrace}
>  * Trace File Format::          GDB trace file format
>  * Index Section Format::        .gdb_index section format
>  * Man Pages::                  Manual pages
> @@ -45629,6 +45632,146 @@ should contain a comma-separated list of cores that this process
>  is running on.  Target may provide additional columns,
>  which @value{GDBN} currently ignores.
>
> +@node Linux kernel ptrace restrictions
> +@appendix Linux kernel @code{ptrace} restrictions
> +@cindex linux kernel ptrace restrictions, attach
> +
> +The @code{ptrace} system call is used by @value{GDBN} and
> +@code{gdbserver} on GNU/Linux to, among other things, attach to a new
> +or existing inferior in order to start debugging it.  Due to security
> +concerns, some distributions and vendors disable or severely restrict
> +the ability to perform these operations, which can make @value{GDBN}
> +or @code{gdbserver} malfunction.  In this section, we will expand on
> +how this malfunction can manifest itself, and how to modify the
> +system's settings in order to be able to use @value{GDBN} and
> +@code{gdbserver} properly.
> +
> +@menu
> +* The error message::                   The error message displayed when the
> +                                        system prevents @value{GDBN}
> +                                        or @code{gdbserver} from using
> +                                        @code{ptrace}
> +* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
> +* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
> +* Docker and seccomp::                  Docker and the @code{seccomp}
> +                                        infrastructure
> +@end menu
> +
> +@node The error message
> +@appendixsection The error message
> +
> +When the system prevents @value{GDBN} or @code{gdbserver} from using
> +the @code{ptrace} system call, you will likely see a descriptive error
> +message explaining what is wrong and how to attempt to fix the
> +problem.  For example, when SELinux's @code{deny_ptrace} option is
> +enabled, you can see:
> +
> +@smallexample
> +$ gdb program
> +...
> +(@value{GDBP}) run
> +Starting program: program
> +warning: Could not trace the inferior process.
> +Error:
> +warning: ptrace: Permission denied
> +The SELinux 'deny_ptrace' option is enabled and preventing @value{GDBN}
> +from using 'ptrace'.  You can disable it by executing (as root):
> +
> +  setsebool deny_ptrace off
> +
> +If you are debugging the inferior remotely, the instruction(s) above must
> +be performed in the target system (e.g., where GDBserver is running).
> +During startup program exited with code 127.
> +(@value{GDBP})
> +@end smallexample
> +
> +Sometimes, it may not be possible to acquire the necessary data to
> +determine the root cause of the failure.  In this case, you will see a
> +generic error message pointing you to this section:
> +
> +@smallexample
> +$ gdb program
> +...
> +Starting program: program
> +warning: Could not trace the inferior process.
> +Error:
> +warning: ptrace: Permission denied
> +There might be restrictions preventing ptrace from working.  Please see
> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
> +for more details.
> +During startup program exited with code 127.
> +(@value{GDBP})
> +@end smallexample
> +
> +@node SELinux's deny_ptrace
> +@appendixsection SELinux's @code{deny_ptrace}
> +@cindex SELinux
> +@cindex deny_ptrace
> +
> +If you are using SELinux, you might want to check whether the
> +@code{deny_ptrace} option is enabled by doing:
> +
> +@smallexample
> +$ getsebool deny_ptrace
> +deny_ptrace --> on
> +@end smallexample
> +
> +If the option is enabled, you can disable it by doing, as root:
> +
> +@smallexample
> +# setsebool deny_ptrace off
> +@end smallexample
> +
> +The option will be disabled until the next reboot.  If you would like
> +to disable it permanently, you can do (as root):
> +
> +@smallexample
> +# setsebool -P deny_ptrace off
> +@end smallexample
> +
> +@node Yama's ptrace_scope
> +@appendixsection Yama's @code{ptrace_scope}
> +@cindex yama, ptrace_scope
> +
> +If your system has Yama enabled, you might want to check whether the
> +@code{ptrace_scope} setting is enabled by checking the value of
> +@file{/proc/sys/kernel/yama/ptrace_scope}:
> +
> +@smallexample
> +$ cat /proc/sys/kernel/yama/ptrace_scope
> +0
> +@end smallexample
> +
> +If you see anything other than @code{0}, @value{GDBN} or
> +@code{gdbserver} can be affected by it.  You can temporarily disable
> +the feature by doing, as root:
> +
> +@smallexample
> +# sysctl kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample
> +
> +You can make this permanent by doing, as root:
> +
> +@smallexample
> +# sysctl -w kernel.yama.ptrace_scope=0
> +kernel.yama.ptrace_scope = 0
> +@end smallexample

Actually, sysctl's "-w" option doesn't make the setting permanent. It
just lets one write the value. sysctl(8) says about the
"variable=value" syntax:
 > This requires the -w parameter to use.
Though I've found that omitting "-w" works exactly the same on
procps-ng 3.3.9 — checked with strace, which gives identical output in
both cases.
In any case, to make this permanent, one has to modify /etc/sysctl.*
locations (namely, on Ubuntu 14.04 it's /etc/sysctl.d/10-ptrace.conf
).

> +
> +@node Docker and seccomp
> +@appendixsection Docker and @code{seccomp}
> +@cindex docker, seccomp
> +
> +If you are using Docker (@uref{https://www.docker.com/}) containers,
> +you will probably have to disable its @code{seccomp} protections in
> +order to be able to use @value{GDBN} or @code{gdbserver}.  To do that,
> +you can use the options @code{--cap-add=SYS_PTRACE --security-opt
> +seccomp=unconfined} when invoking Docker:
> +
> +@smallexample
> +$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
> +@end smallexample
> +
>  @node Trace File Format
>  @appendix Trace File Format
>  @cindex trace file format
> --
> 2.24.1
>

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 5/6] Document Linux-specific possible ptrace restrictions
  2020-02-26 21:00       ` Ruslan Kabatsayev
@ 2020-02-26 22:08         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-26 22:08 UTC (permalink / raw)
  To: Ruslan Kabatsayev; +Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii

On Wednesday, February 26 2020, Ruslan Kabatsayev wrote:

> On Wed, 26 Feb 2020 at 23:06, Sergio Durigan Junior <sergiodj@redhat.com> wrote:
>>
>> This patch creates a new "Linux kernel ptrace restrictions" which
>> documents possible causes that can be prevent the inferior from being
>> correctly started/debugged.
>>
>> This has been pre-approved by Eli.
>>
>> gdb/doc/ChangeLog:
>> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>>
>>         * gdb.texinfo (Linux kernel ptrace restrictions): New appendix
>>         section.
>> ---
>>  gdb/doc/gdb.texinfo | 143 ++++++++++++++++++++++++++++++++++++++++++++
>>  1 file changed, 143 insertions(+)
>>
>> diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
>> index f1798e35b5..a95158d5d3 100644
>> --- a/gdb/doc/gdb.texinfo
>> +++ b/gdb/doc/gdb.texinfo
>> @@ -182,6 +182,9 @@ software in general.  We will miss him.
>>                                  @value{GDBN}
>>  * Operating System Information:: Getting additional information from
>>                                   the operating system
>> +* Linux kernel ptrace restrictions::        Restrictions sometimes
>> +                                            imposed by the Linux
>> +                                            kernel on @code{ptrace}
>>  * Trace File Format::          GDB trace file format
>>  * Index Section Format::        .gdb_index section format
>>  * Man Pages::                  Manual pages
>> @@ -45629,6 +45632,146 @@ should contain a comma-separated list of cores that this process
>>  is running on.  Target may provide additional columns,
>>  which @value{GDBN} currently ignores.
>>
>> +@node Linux kernel ptrace restrictions
>> +@appendix Linux kernel @code{ptrace} restrictions
>> +@cindex linux kernel ptrace restrictions, attach
>> +
>> +The @code{ptrace} system call is used by @value{GDBN} and
>> +@code{gdbserver} on GNU/Linux to, among other things, attach to a new
>> +or existing inferior in order to start debugging it.  Due to security
>> +concerns, some distributions and vendors disable or severely restrict
>> +the ability to perform these operations, which can make @value{GDBN}
>> +or @code{gdbserver} malfunction.  In this section, we will expand on
>> +how this malfunction can manifest itself, and how to modify the
>> +system's settings in order to be able to use @value{GDBN} and
>> +@code{gdbserver} properly.
>> +
>> +@menu
>> +* The error message::                   The error message displayed when the
>> +                                        system prevents @value{GDBN}
>> +                                        or @code{gdbserver} from using
>> +                                        @code{ptrace}
>> +* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
>> +* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
>> +* Docker and seccomp::                  Docker and the @code{seccomp}
>> +                                        infrastructure
>> +@end menu
>> +
>> +@node The error message
>> +@appendixsection The error message
>> +
>> +When the system prevents @value{GDBN} or @code{gdbserver} from using
>> +the @code{ptrace} system call, you will likely see a descriptive error
>> +message explaining what is wrong and how to attempt to fix the
>> +problem.  For example, when SELinux's @code{deny_ptrace} option is
>> +enabled, you can see:
>> +
>> +@smallexample
>> +$ gdb program
>> +...
>> +(@value{GDBP}) run
>> +Starting program: program
>> +warning: Could not trace the inferior process.
>> +Error:
>> +warning: ptrace: Permission denied
>> +The SELinux 'deny_ptrace' option is enabled and preventing @value{GDBN}
>> +from using 'ptrace'.  You can disable it by executing (as root):
>> +
>> +  setsebool deny_ptrace off
>> +
>> +If you are debugging the inferior remotely, the instruction(s) above must
>> +be performed in the target system (e.g., where GDBserver is running).
>> +During startup program exited with code 127.
>> +(@value{GDBP})
>> +@end smallexample
>> +
>> +Sometimes, it may not be possible to acquire the necessary data to
>> +determine the root cause of the failure.  In this case, you will see a
>> +generic error message pointing you to this section:
>> +
>> +@smallexample
>> +$ gdb program
>> +...
>> +Starting program: program
>> +warning: Could not trace the inferior process.
>> +Error:
>> +warning: ptrace: Permission denied
>> +There might be restrictions preventing ptrace from working.  Please see
>> +the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>> +for more details.
>> +During startup program exited with code 127.
>> +(@value{GDBP})
>> +@end smallexample
>> +
>> +@node SELinux's deny_ptrace
>> +@appendixsection SELinux's @code{deny_ptrace}
>> +@cindex SELinux
>> +@cindex deny_ptrace
>> +
>> +If you are using SELinux, you might want to check whether the
>> +@code{deny_ptrace} option is enabled by doing:
>> +
>> +@smallexample
>> +$ getsebool deny_ptrace
>> +deny_ptrace --> on
>> +@end smallexample
>> +
>> +If the option is enabled, you can disable it by doing, as root:
>> +
>> +@smallexample
>> +# setsebool deny_ptrace off
>> +@end smallexample
>> +
>> +The option will be disabled until the next reboot.  If you would like
>> +to disable it permanently, you can do (as root):
>> +
>> +@smallexample
>> +# setsebool -P deny_ptrace off
>> +@end smallexample
>> +
>> +@node Yama's ptrace_scope
>> +@appendixsection Yama's @code{ptrace_scope}
>> +@cindex yama, ptrace_scope
>> +
>> +If your system has Yama enabled, you might want to check whether the
>> +@code{ptrace_scope} setting is enabled by checking the value of
>> +@file{/proc/sys/kernel/yama/ptrace_scope}:
>> +
>> +@smallexample
>> +$ cat /proc/sys/kernel/yama/ptrace_scope
>> +0
>> +@end smallexample
>> +
>> +If you see anything other than @code{0}, @value{GDBN} or
>> +@code{gdbserver} can be affected by it.  You can temporarily disable
>> +the feature by doing, as root:
>> +
>> +@smallexample
>> +# sysctl kernel.yama.ptrace_scope=0
>> +kernel.yama.ptrace_scope = 0
>> +@end smallexample
>> +
>> +You can make this permanent by doing, as root:
>> +
>> +@smallexample
>> +# sysctl -w kernel.yama.ptrace_scope=0
>> +kernel.yama.ptrace_scope = 0
>> +@end smallexample
>
> Actually, sysctl's "-w" option doesn't make the setting permanent. It
> just lets one write the value. sysctl(8) says about the
> "variable=value" syntax:
>  > This requires the -w parameter to use.
> Though I've found that omitting "-w" works exactly the same on
> procps-ng 3.3.9 — checked with strace, which gives identical output in
> both cases.
> In any case, to make this permanent, one has to modify /etc/sysctl.*
> locations (namely, on Ubuntu 14.04 it's /etc/sysctl.d/10-ptrace.conf
> ).

Thanks.  I'll remove this part from the docs, then.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 6/6] Fix comment for 'gdb_dlopen'
  2020-02-26 20:06     ` [PATCH 6/6] Fix comment for 'gdb_dlopen' Sergio Durigan Junior
  2020-02-26 20:23       ` Christian Biesinger via gdb-patches
@ 2020-02-28 15:21       ` Tom Tromey
  2020-02-28 16:05         ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2020-02-28 15:21 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

Sergio> gdbsupport/ChangeLog:
Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

Sergio> 	* gdb-dlfcn.h (gdb_dlopen): Update comment.

This one could go in immediately under the obvious rule.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-26 20:06     ` [PATCH 1/6] Introduce scoped_pipe.h Sergio Durigan Junior
@ 2020-02-28 15:23       ` Tom Tromey
  2020-02-28 16:08         ` Sergio Durigan Junior
  2020-02-28 19:20       ` Pedro Alves
  1 sibling, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2020-02-28 15:23 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

Sergio> gdb/ChangeLog:
Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

Sergio> 	* unittests/scoped_pipe-selftests.c: New file.

Sergio> gdbsupport/ChangeLog:
Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

Sergio> 	* scoped_pipe.h: New file.

I think new general-purpose code should go in gdbsupport.

Unfortunately right now we don't have a good way to put the unit tests
there.  This is something we ought to do eventually though.

Sergio> +#ifndef COMMON_SCOPED_PIPE_H

I think we've been using GDBSUPPORT rather than COMMON now.

Sergio> +  explicit scoped_pipe ()

I think explicit is only needed for 1-arg constructors.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-26 20:06     ` [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
@ 2020-02-28 15:29       ` Tom Tromey
  2020-02-28 16:36         ` Sergio Durigan Junior
  2020-02-28 20:06         ` Pedro Alves
  2020-02-28 19:49       ` Pedro Alves
  1 sibling, 2 replies; 98+ messages in thread
From: Tom Tromey @ 2020-02-28 15:29 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

Sergio> Since this hunk may be a bit controversial, I decided to split it into
Sergio> a separate patch.  This is going to be needed by the ptrace-error
Sergio> feature; GDB will need to be able to access the value of errno even
Sergio> after a call to our 'perror'-like functions.

I'm in favor of this.  The existing code seems pretty ugly.

I'd imagine it's unlikely that any caller would rely on this.
If it tested cleanly then that is good enough for me.

Sergio> Another small hunk is the one that saves/restores errno on gdbserver's
Sergio> 'perror_with_name', but this one is pretty trivial, I think.

I didn't understand why this one was needed.
Does safe_strerror reset errno?  Maybe a comment would be in order.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 6/6] Fix comment for 'gdb_dlopen'
  2020-02-28 15:21       ` Tom Tromey
@ 2020-02-28 16:05         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 16:05 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
> Sergio> gdbsupport/ChangeLog:
> Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> Sergio> 	* gdb-dlfcn.h (gdb_dlopen): Update comment.
>
> This one could go in immediately under the obvious rule.

Thanks, Tom.

Pushed: d7592e974706637058867b02626c52a30ef0a2ee

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-28 15:23       ` Tom Tromey
@ 2020-02-28 16:08         ` Sergio Durigan Junior
  2020-02-28 18:57           ` Tom Tromey
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 16:08 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
> Sergio> gdb/ChangeLog:
> Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> Sergio> 	* unittests/scoped_pipe-selftests.c: New file.
>
> Sergio> gdbsupport/ChangeLog:
> Sergio> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
>
> Sergio> 	* scoped_pipe.h: New file.
>
> I think new general-purpose code should go in gdbsupport.

Yep.  scoped_pipe.h is there.

> Unfortunately right now we don't have a good way to put the unit tests
> there.  This is something we ought to do eventually though.

Right, that's something I noticed as well.

> Sergio> +#ifndef COMMON_SCOPED_PIPE_H
>
> I think we've been using GDBSUPPORT rather than COMMON now.

Ah, OK.  I found other places using COMMON, but I'll change it here.

> Sergio> +  explicit scoped_pipe ()
>
> I think explicit is only needed for 1-arg constructors.

OK, I'll remove the keyword.

I'll make the changes locally.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 15:29       ` Tom Tromey
@ 2020-02-28 16:36         ` Sergio Durigan Junior
  2020-02-28 18:58           ` Tom Tromey
  2020-02-28 20:06         ` Pedro Alves
  1 sibling, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 16:36 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
> Sergio> Since this hunk may be a bit controversial, I decided to split it into
> Sergio> a separate patch.  This is going to be needed by the ptrace-error
> Sergio> feature; GDB will need to be able to access the value of errno even
> Sergio> after a call to our 'perror'-like functions.
>
> I'm in favor of this.  The existing code seems pretty ugly.
>
> I'd imagine it's unlikely that any caller would rely on this.
> If it tested cleanly then that is good enough for me.

As far as I have tested (buildbot and locally), everything is OK.

> Sergio> Another small hunk is the one that saves/restores errno on gdbserver's
> Sergio> 'perror_with_name', but this one is pretty trivial, I think.
>
> I didn't understand why this one was needed.
> Does safe_strerror reset errno?  Maybe a comment would be in order.

Ah, no, safe_strerror doesn't reset errno.  As I explained in the commit
message, it is not allowed to do so.  The reason I decided to explicitly
save/restore errno is because I wanted to make sure that we (also
explicitly) follow the same rules for both GDB and gdbserver perror
functions.  But now I'm left thinking that I should have proposed the
same thing for 'throw_perror_with_name'...

Anyway, I'm fine with removing the save/restore from gdbserver's
perror.  I'll also remove the reference to it in the commit message.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-28 16:08         ` Sergio Durigan Junior
@ 2020-02-28 18:57           ` Tom Tromey
  2020-02-28 19:48             ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2020-02-28 18:57 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: Tom Tromey, GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

>> I think new general-purpose code should go in gdbsupport.

Sergio> Yep.  scoped_pipe.h is there.

Lol, sorry about that.

Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 16:36         ` Sergio Durigan Junior
@ 2020-02-28 18:58           ` Tom Tromey
  2020-02-28 19:50             ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Tom Tromey @ 2020-02-28 18:58 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: Tom Tromey, GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:

>> I didn't understand why this one was needed.
>> Does safe_strerror reset errno?  Maybe a comment would be in order.

Sergio> Ah, no, safe_strerror doesn't reset errno.  As I explained in the commit
Sergio> message, it is not allowed to do so.  The reason I decided to explicitly
Sergio> save/restore errno is because I wanted to make sure that we (also
Sergio> explicitly) follow the same rules for both GDB and gdbserver perror
Sergio> functions.  But now I'm left thinking that I should have proposed the
Sergio> same thing for 'throw_perror_with_name'...

Sergio> Anyway, I'm fine with removing the save/restore from gdbserver's
Sergio> perror.  I'll also remove the reference to it in the commit message.

The main thing for me is that when someone sees this in 5 years, that
there's some idea of why the line is there.  Leaving the line out is
just as good :)

thanks,
Tom

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-26 20:06     ` [PATCH 1/6] Introduce scoped_pipe.h Sergio Durigan Junior
  2020-02-28 15:23       ` Tom Tromey
@ 2020-02-28 19:20       ` Pedro Alves
  2020-02-28 19:47         ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2020-02-28 19:20 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On 2/26/20 8:05 PM, Sergio Durigan Junior wrote:

> --- /dev/null
> +++ b/gdb/unittests/scoped_pipe-selftests.c
> @@ -0,0 +1,96 @@

> +
> +#include "defs.h"
> +
> +#include "gdbsupport/scoped_pipe.h"
> +#include "config.h"

Was there a reason for this "config.h" include?

Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-28 19:20       ` Pedro Alves
@ 2020-02-28 19:47         ` Sergio Durigan Junior
  2020-02-28 20:07           ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 19:47 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Pedro Alves wrote:

> On 2/26/20 8:05 PM, Sergio Durigan Junior wrote:
>
>> --- /dev/null
>> +++ b/gdb/unittests/scoped_pipe-selftests.c
>> @@ -0,0 +1,96 @@
>
>> +
>> +#include "defs.h"
>> +
>> +#include "gdbsupport/scoped_pipe.h"
>> +#include "config.h"
>
> Was there a reason for this "config.h" include?

I copied this test from the existing scoped_fd-selftests.c and adjusted
it.  I did remove other includes, but left this one untouched.  I tried
removing it now, and the file builds just fine, so I'll update my copy
of the patch here.  Thanks!

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-28 18:57           ` Tom Tromey
@ 2020-02-28 19:48             ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 19:48 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Tom Tromey wrote:

>>> I think new general-purpose code should go in gdbsupport.
>
> Sergio> Yep.  scoped_pipe.h is there.
>
> Lol, sorry about that.

That's alright!  I was confused with your comment, but wasn't sure if
you meant something else by it ;-).

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-26 20:06     ` [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
  2020-02-28 15:29       ` Tom Tromey
@ 2020-02-28 19:49       ` Pedro Alves
  2020-02-28 20:01         ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2020-02-28 19:49 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches
  Cc: Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On 2/26/20 8:05 PM, Sergio Durigan Junior wrote:
> 
> git blame tells me that this piece of code is pretty old; the commit
> that "introduced" it is:
> 
>   commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc
>   Author: Stan Shebs <shebs@codesourcery.com>
>   Date:   Fri Apr 16 01:35:26 1999 +0000
> 
>       Initial creation of sourceware repository
> 
> so yeah...

This is not the oldest commit in the tree.  Using git log
starting at the hash, you should be able to find older commits
which touch the file.  The thing is that history around the time
of that "initial creation" commit, is quite messy, because the 
CVS tree back then was deleted and recreated a number of times
and merged in some odd ways.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 18:58           ` Tom Tromey
@ 2020-02-28 19:50             ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 19:50 UTC (permalink / raw)
  To: Tom Tromey; +Cc: GDB Patches, Pedro Alves, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Tom Tromey wrote:

>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>
>>> I didn't understand why this one was needed.
>>> Does safe_strerror reset errno?  Maybe a comment would be in order.
>
> Sergio> Ah, no, safe_strerror doesn't reset errno.  As I explained in the commit
> Sergio> message, it is not allowed to do so.  The reason I decided to explicitly
> Sergio> save/restore errno is because I wanted to make sure that we (also
> Sergio> explicitly) follow the same rules for both GDB and gdbserver perror
> Sergio> functions.  But now I'm left thinking that I should have proposed the
> Sergio> same thing for 'throw_perror_with_name'...
>
> Sergio> Anyway, I'm fine with removing the save/restore from gdbserver's
> Sergio> perror.  I'll also remove the reference to it in the commit message.
>
> The main thing for me is that when someone sees this in 5 years, that
> there's some idea of why the line is there.  Leaving the line out is
> just as good :)

Right, that's an excellent argument.  It's safe to leave the line out
right now; the important thing is that we don't touch errno, anyway.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 19:49       ` Pedro Alves
@ 2020-02-28 20:01         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 20:01 UTC (permalink / raw)
  To: Pedro Alves; +Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Pedro Alves wrote:

> On 2/26/20 8:05 PM, Sergio Durigan Junior wrote:
>> 
>> git blame tells me that this piece of code is pretty old; the commit
>> that "introduced" it is:
>> 
>>   commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc
>>   Author: Stan Shebs <shebs@codesourcery.com>
>>   Date:   Fri Apr 16 01:35:26 1999 +0000
>> 
>>       Initial creation of sourceware repository
>> 
>> so yeah...
>
> This is not the oldest commit in the tree.  Using git log
> starting at the hash, you should be able to find older commits
> which touch the file.  The thing is that history around the time
> of that "initial creation" commit, is quite messy, because the 
> CVS tree back then was deleted and recreated a number of times
> and merged in some odd ways.

Sorry, I didn't mean to say that this is the oldest commit in the tree.
But even doing a bit more search, I could only find this commit:

  commit 8eec331072987d38064745a33ae89cc5d195029c
  Author: Steve Chamberlain <sac@cygnus>
  Date:   Sat Mar 19 03:16:10 1994 +0000

              * utils.c (prompt_for_continue): Call readline, not gdb_readline.

which did:

  -  bfd_error = no_error;
  +  bfd_set_error (bfd_error_no_error);

This means the code is really old (pre-1994), and I could not track when
it was added.  And I don't think it matters much at this point: even if
I could find the exact commit that added it, I doubt I'd be able to find
the rationale.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 15:29       ` Tom Tromey
  2020-02-28 16:36         ` Sergio Durigan Junior
@ 2020-02-28 20:06         ` Pedro Alves
  2020-02-28 20:35           ` Sergio Durigan Junior
  1 sibling, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2020-02-28 20:06 UTC (permalink / raw)
  To: Tom Tromey, Sergio Durigan Junior
  Cc: GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 2/28/20 3:29 PM, Tom Tromey wrote:
>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
> 
> Sergio> Since this hunk may be a bit controversial, I decided to split it into
> Sergio> a separate patch.  This is going to be needed by the ptrace-error
> Sergio> feature; GDB will need to be able to access the value of errno even
> Sergio> after a call to our 'perror'-like functions.
> 
> I'm in favor of this.  The existing code seems pretty ugly.

I'm not sure in favor of relying on errno being preserved from
throw site to catch site, with potentially multiple try/catch hops
in between.  Sergio, can you point out exactly how you're
intending to use that?

But, I'm in favor of removing this errno/bfd_error clearing too.

We used to have more global state clearing around this area,
but it's been gradually removed.  E.g.:

commit 0af679c6e0645a93d5a60ec936b94dc70a2f9e5c
Author:     Pedro Alves <palves@redhat.com>
AuthorDate: Tue Apr 12 16:49:30 2016 +0100
Commit:     Pedro Alves <palves@redhat.com>
CommitDate: Tue Apr 12 16:55:35 2016 +0100

    Don't call clear_quit_flag in prepare_to_throw_exception
    
    I think this is reminiscent of the time when a longjmp would always
    jump to the top level.  Nowaways code that throw exceptions other than
    a quit, which may even be caught and handled without reaching the top
    level.  Certainly such exceptions shouldn't clear an interrupt
    request...


I suspect this perror_with_name errno/bfd_error clearing was also
related to ancient direct longjmp to the top level.

> I'd imagine it's unlikely that any caller would rely on this.
> If it tested cleanly then that is good enough for me.




> 
> Sergio> Another small hunk is the one that saves/restores errno on gdbserver's
> Sergio> 'perror_with_name', but this one is pretty trivial, I think.
> 
> I didn't understand why this one was needed.
> Does safe_strerror reset errno?  Maybe a comment would be in order.
Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 1/6] Introduce scoped_pipe.h
  2020-02-28 19:47         ` Sergio Durigan Junior
@ 2020-02-28 20:07           ` Pedro Alves
  0 siblings, 0 replies; 98+ messages in thread
From: Pedro Alves @ 2020-02-28 20:07 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: GDB Patches, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On 2/28/20 7:47 PM, Sergio Durigan Junior wrote:
> On Friday, February 28 2020, Pedro Alves wrote:
> 
>> On 2/26/20 8:05 PM, Sergio Durigan Junior wrote:
>>

>>> --- /dev/null
>>> +++ b/gdb/unittests/scoped_pipe-selftests.c
>>> @@ -0,0 +1,96 @@
>>
>>> +
>>> +#include "defs.h"
>>> +
>>> +#include "gdbsupport/scoped_pipe.h"
>>> +#include "config.h"
>>
>> Was there a reason for this "config.h" include?
> 
> I copied this test from the existing scoped_fd-selftests.c and adjusted
> it.  I did remove other includes, but left this one untouched.  I tried
> removing it now, and the file builds just fine, so I'll update my copy
> of the patch here.  Thanks!

Ah, I see.  That config.h include shouldn't be there in
scoped_fd-selftests.c either.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 20:06         ` Pedro Alves
@ 2020-02-28 20:35           ` Sergio Durigan Junior
  2020-02-28 21:11             ` Pedro Alves
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-02-28 20:35 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Pedro Alves wrote:

> On 2/28/20 3:29 PM, Tom Tromey wrote:
>>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>> 
>> Sergio> Since this hunk may be a bit controversial, I decided to split it into
>> Sergio> a separate patch.  This is going to be needed by the ptrace-error
>> Sergio> feature; GDB will need to be able to access the value of errno even
>> Sergio> after a call to our 'perror'-like functions.
>> 
>> I'm in favor of this.  The existing code seems pretty ugly.
>
> I'm not sure in favor of relying on errno being preserved from
> throw site to catch site, with potentially multiple try/catch hops
> in between.  Sergio, can you point out exactly how you're
> intending to use that?

Yeah.  I caught this problem when I was testing to see if GDB would
print the ptrace fail reason when trying (unsuccessfully) to attach to a
process.

The call stack looks like:

  linux_nat_target::attach
    |
    |--> inf_ptrace_target::attach # where ptrace fails
         |
         |--> throw_perror_with_name # where errno is set to 0

When 'throw_perror_with_name' calls 'error', 'linux_nat_target::attach'
catches the exception and tries to print the reason:

  try
    {
      inf_ptrace_target::attach (args, from_tty);
    }
  catch (const gdb_exception_error &ex)
    {
      int saved_errno = errno;
      pid_t pid = parse_pid_to_attach (args);
      std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
      ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

      if (!reason.empty ())
	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
		     ex.what ());
      else
	throw_error (ex.error, "%s", ex.what ());
    }

However, at this point errno is already 0, so the function can't
determine the exact reason for the ptrace failure.  In fact, because
errno = 0, 'linux_ptrace_attach_fail_reason' doesn't print anything,
because it thinks everything is OK!

IMO, it doesn't make sense to have errno = 0 while you're handling an
exception (which, in this case, was caused exactly because a syscall
failed, and so we expect errno to be different than 0).

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 20:35           ` Sergio Durigan Junior
@ 2020-02-28 21:11             ` Pedro Alves
  2020-03-02 20:07               ` Sergio Durigan Junior
  0 siblings, 1 reply; 98+ messages in thread
From: Pedro Alves @ 2020-02-28 21:11 UTC (permalink / raw)
  To: Sergio Durigan Junior
  Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On 2/28/20 8:35 PM, Sergio Durigan Junior wrote:
> On Friday, February 28 2020, Pedro Alves wrote:
> 
>> On 2/28/20 3:29 PM, Tom Tromey wrote:
>>>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>>>
>>> Sergio> Since this hunk may be a bit controversial, I decided to split it into
>>> Sergio> a separate patch.  This is going to be needed by the ptrace-error
>>> Sergio> feature; GDB will need to be able to access the value of errno even
>>> Sergio> after a call to our 'perror'-like functions.
>>>
>>> I'm in favor of this.  The existing code seems pretty ugly.
>>
>> I'm not sure in favor of relying on errno being preserved from
>> throw site to catch site, with potentially multiple try/catch hops
>> in between.  Sergio, can you point out exactly how you're
>> intending to use that?
> 
> Yeah.  I caught this problem when I was testing to see if GDB would
> print the ptrace fail reason when trying (unsuccessfully) to attach to a
> process.
> 
> The call stack looks like:
> 
>   linux_nat_target::attach
>     |
>     |--> inf_ptrace_target::attach # where ptrace fails
>          |
>          |--> throw_perror_with_name # where errno is set to 0
> 
> When 'throw_perror_with_name' calls 'error', 'linux_nat_target::attach'
> catches the exception and tries to print the reason:
> 
>   try
>     {
>       inf_ptrace_target::attach (args, from_tty);
>     }
>   catch (const gdb_exception_error &ex)
>     {
>       int saved_errno = errno;
>       pid_t pid = parse_pid_to_attach (args);
>       std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
> 
>       if (!reason.empty ())
> 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
> 		     ex.what ());
>       else
> 	throw_error (ex.error, "%s", ex.what ());
>     }
> 
> However, at this point errno is already 0, so the function can't
> determine the exact reason for the ptrace failure.  In fact, because
> errno = 0, 'linux_ptrace_attach_fail_reason' doesn't print anything,
> because it thinks everything is OK!
> 
> IMO, it doesn't make sense to have errno = 0 while you're handling an
> exception (which, in this case, was caused exactly because a syscall
> failed, and so we expect errno to be different than 0).

This is bad design.  Exception objects should be self contained
and not rely on global state to transfer information.

E.g., it should be possible to do

void my_function ()
{
  try 
    {
       function_that_throws ();
    }
  catch (const gdb_exception &ex)
    {
       // call some function that potentially changes errno.
       function_that_changes_errno ();
  
       throw; // rethrow.
    }
}

and then:

try
  {
     my_function ();
  }
catch (const gdb_exception &ex)
  {
     // errno here is really unreliable.
  }

It's not even guaranteed that the exception thrown from
within inf_ptrace_target::attach is thrown after setting
errno, or that errno is meaningful for the exception thrown.

For example, this error:

  if (pid == getpid ())
    error (_("I refuse to debug myself!"));


I think the simpler thing to do here is to change
inf_ptrace_target::attach to return the ptrace errno
as the function's return.  I.e., rename it and change it
from:

 void inf_ptrace_target::attach (const char *args, int from_tty);

to:

 int inf_ptrace_target::ptrace_attach (const char *args, int from_tty);

And change the body like this:

  errno = 0;
  ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
  if (errno != 0)
-   perror_with_name (("ptrace"));
+   return errno;

with a return 0 added at the end.

All other errors still throw like before.

Then add a new "attach" method that wraps the old method, so that
targets that inherit inf_ptrace_target and don't override attach
continue working:

 void
 inf_ptrace_target::attach (const char *args, int from_tty)
 {
   errno = ptrace_attach (args, from_tty);
   if (errno != 0)
     perror_with_name (("ptrace"));
 }

This avoids having to think about storing the errno number in
the exception object.

Thanks,
Pedro Alves

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-02-28 21:11             ` Pedro Alves
@ 2020-03-02 20:07               ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-02 20:07 UTC (permalink / raw)
  To: Pedro Alves; +Cc: Tom Tromey, GDB Patches, Eli Zaretskii, Ruslan Kabatsayev

On Friday, February 28 2020, Pedro Alves wrote:

> On 2/28/20 8:35 PM, Sergio Durigan Junior wrote:
>> On Friday, February 28 2020, Pedro Alves wrote:
>> 
>>> On 2/28/20 3:29 PM, Tom Tromey wrote:
>>>>>>>>> "Sergio" == Sergio Durigan Junior <sergiodj@redhat.com> writes:
>>>>
>>>> Sergio> Since this hunk may be a bit controversial, I decided to split it into
>>>> Sergio> a separate patch.  This is going to be needed by the ptrace-error
>>>> Sergio> feature; GDB will need to be able to access the value of errno even
>>>> Sergio> after a call to our 'perror'-like functions.
>>>>
>>>> I'm in favor of this.  The existing code seems pretty ugly.
>>>
>>> I'm not sure in favor of relying on errno being preserved from
>>> throw site to catch site, with potentially multiple try/catch hops
>>> in between.  Sergio, can you point out exactly how you're
>>> intending to use that?
>> 
>> Yeah.  I caught this problem when I was testing to see if GDB would
>> print the ptrace fail reason when trying (unsuccessfully) to attach to a
>> process.
>> 
>> The call stack looks like:
>> 
>>   linux_nat_target::attach
>>     |
>>     |--> inf_ptrace_target::attach # where ptrace fails
>>          |
>>          |--> throw_perror_with_name # where errno is set to 0
>> 
>> When 'throw_perror_with_name' calls 'error', 'linux_nat_target::attach'
>> catches the exception and tries to print the reason:
>> 
>>   try
>>     {
>>       inf_ptrace_target::attach (args, from_tty);
>>     }
>>   catch (const gdb_exception_error &ex)
>>     {
>>       int saved_errno = errno;
>>       pid_t pid = parse_pid_to_attach (args);
>>       std::string reason = linux_ptrace_attach_fail_reason (pid, saved_errno);
>>       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
>> 
>>       if (!reason.empty ())
>> 	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
>> 		     ex.what ());
>>       else
>> 	throw_error (ex.error, "%s", ex.what ());
>>     }
>> 
>> However, at this point errno is already 0, so the function can't
>> determine the exact reason for the ptrace failure.  In fact, because
>> errno = 0, 'linux_ptrace_attach_fail_reason' doesn't print anything,
>> because it thinks everything is OK!
>> 
>> IMO, it doesn't make sense to have errno = 0 while you're handling an
>> exception (which, in this case, was caused exactly because a syscall
>> failed, and so we expect errno to be different than 0).
>
> This is bad design.  Exception objects should be self contained
> and not rely on global state to transfer information.

OK.  I implemented your idea, but I will wait until the other patches
are reviewed so I can submit a v2 of the whole series.

Thanks.

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/

^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 0/6] Improve ptrace-error detection
       [not found]     ` <87v9nh3yme.fsf@redhat.com>
@ 2020-03-15  4:21       ` Sergio Durigan Junior
  2020-03-15 21:16         ` Kevin Buettner
  0 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-15  4:21 UTC (permalink / raw)
  To: GDB Patches; +Cc: Pedro Alves, Tom Tromey, Eli Zaretskii, Ruslan Kabatsayev

On Friday, March 06 2020, I wrote:

> On Wednesday, February 26 2020, I wrote:
>
>> This patch series is the continuation of:
>>
>>   [PATCH v5] Improve ptrace-error detection on Linux targets
>>   https://sourceware.org/ml/gdb-patches/2019-09/msg00504.html
>
> Ping.  Some of the patch have not been reviewed yet.  I can submit a v2
> with an updated version of the series if needed.

Ping^2.

> Thanks.
>
>> I decided to start a new series because this involved a rewrite of
>> several parts of the patch.  I addressed all of the comments I
>> received back in September (mostly from Pedro), but I also did some
>> improvements, especially in the 'fork_inferior' code.
>>
>> After submitting this series to our Buildbot, no regressions were
>> found.
>>
>> Sergio Durigan Junior (6):
>>   Introduce scoped_pipe.h
>>   Don't reset errno/bfd_error on 'throw_perror_with_name'
>>   Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
>>   Extend GNU/Linux to check for ptrace error
>>   Document Linux-specific possible ptrace restrictions
>>   Fix comment for 'gdb_dlopen'
>>
>>  gdb/Makefile.in                       |   1 +
>>  gdb/doc/gdb.texinfo                   | 143 ++++++++++++++++
>>  gdb/inf-ptrace.c                      |  18 +-
>>  gdb/linux-nat.c                       |  10 +-
>>  gdb/nat/fork-inferior.c               | 231 ++++++++++++++++++++++++--
>>  gdb/nat/fork-inferior.h               |  87 ++++++++--
>>  gdb/nat/linux-ptrace.c                | 178 +++++++++++++++++++-
>>  gdb/nat/linux-ptrace.h                |  27 ++-
>>  gdb/remote.c                          |  40 ++++-
>>  gdb/unittests/scoped_pipe-selftests.c |  96 +++++++++++
>>  gdb/utils.c                           |   6 -
>>  gdbserver/linux-low.cc                |  31 +++-
>>  gdbserver/server.cc                   |  38 ++++-
>>  gdbserver/thread-db.cc                |   2 +-
>>  gdbserver/utils.cc                    |   2 +
>>  gdbsupport/gdb-dlfcn.h                |   4 +-
>>  gdbsupport/scoped_pipe.h              |  63 +++++++
>>  17 files changed, 909 insertions(+), 68 deletions(-)
>>  create mode 100644 gdb/unittests/scoped_pipe-selftests.c
>>  create mode 100644 gdbsupport/scoped_pipe.h
>>
>> -- 
>> 2.24.1
>
> -- 
> Sergio
> GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
> Please send encrypted e-mail if possible
> http://sergiodj.net/

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH 0/6] Improve ptrace-error detection
  2020-03-15  4:21       ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
@ 2020-03-15 21:16         ` Kevin Buettner
  0 siblings, 0 replies; 98+ messages in thread
From: Kevin Buettner @ 2020-03-15 21:16 UTC (permalink / raw)
  To: Sergio Durigan Junior via Gdb-patches

On Sun, 15 Mar 2020 00:21:50 -0400
Sergio Durigan Junior via Gdb-patches <gdb-patches@sourceware.org> wrote:

> > Ping.  Some of the patch have not been reviewed yet.  I can submit a v2
> > with an updated version of the series if needed.  
> 
> Ping^2.

After looking at it for a little while, I think it'd be useful (to me
anyway) to submit a v2 series with all of your local patches applied.

Kevin


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 0/5] Improve ptrace-error detection
  2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
                       ` (6 preceding siblings ...)
       [not found]     ` <87v9nh3yme.fsf@redhat.com>
@ 2020-03-17 15:47     ` Sergio Durigan Junior
  2020-03-17 15:47       ` [PATCH v2 1/5] Introduce scoped_pipe.h Sergio Durigan Junior
                         ` (5 more replies)
  7 siblings, 6 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

Changes from v1:

- Remove "config.h" include from scoped_pipe.h.

- Remove "explicit" keyword from scoped_pipe's constructor.

- Use "GDBSUPPORT" instead of "COMMON" as a prefix for #define on
  scoped_pipe.h.

- Remove errno save/restore dance from
  gdbserver/utils.cc:perror_with_name.

- Implement "ptrace_attach" vs. "attach" idea (from Pedro) on
  inf-ptrace.c in order to make sure we return ptrace errno when
  calling "attach".

Sergio Durigan Junior (5):
  Introduce scoped_pipe.h
  Don't reset errno/bfd_error on 'throw_perror_with_name'
  Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  Extend GNU/Linux to check for ptrace error
  Document Linux-specific possible ptrace restrictions

 gdb/Makefile.in                       |   1 +
 gdb/doc/gdb.texinfo                   | 136 +++++++++++++++
 gdb/inf-ptrace.c                      |  34 +++-
 gdb/inf-ptrace.h                      |   2 +
 gdb/linux-nat.c                       |  24 +--
 gdb/nat/fork-inferior.c               | 231 ++++++++++++++++++++++++--
 gdb/nat/fork-inferior.h               |  87 ++++++++--
 gdb/nat/linux-ptrace.c                | 178 +++++++++++++++++++-
 gdb/nat/linux-ptrace.h                |  27 ++-
 gdb/remote.c                          |  40 ++++-
 gdb/unittests/scoped_pipe-selftests.c |  95 +++++++++++
 gdb/utils.c                           |   6 -
 gdbserver/linux-low.cc                |  31 +++-
 gdbserver/server.cc                   |  38 ++++-
 gdbserver/thread-db.cc                |   2 +-
 gdbsupport/scoped_pipe.h              |  63 +++++++
 16 files changed, 918 insertions(+), 77 deletions(-)
 create mode 100644 gdb/unittests/scoped_pipe-selftests.c
 create mode 100644 gdbsupport/scoped_pipe.h

-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 1/5] Introduce scoped_pipe.h
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
@ 2020-03-17 15:47       ` Sergio Durigan Junior
  2020-03-17 15:47       ` [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
                         ` (4 subsequent siblings)
  5 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

This simple patch introduces gdbsupport/scoped_pipe.h, which is based
on gdbsupport/scoped_fd.h.  When the object is instantiated, a pipe is
created using 'gdb_pipe_cloexec'.  There are two methods (get_read_end
and get_write_end) that allow the user to obtain the read/write ends
of the pipe (no more messing with [0] and [1]), and when the object is
destroyed, the pipe is closed (both ends).

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* unittests/scoped_pipe-selftests.c: New file.

gdbsupport/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* scoped_pipe.h: New file.
---
 gdb/Makefile.in                       |  1 +
 gdb/unittests/scoped_pipe-selftests.c | 95 +++++++++++++++++++++++++++
 gdbsupport/scoped_pipe.h              | 63 ++++++++++++++++++
 3 files changed, 159 insertions(+)
 create mode 100644 gdb/unittests/scoped_pipe-selftests.c
 create mode 100644 gdbsupport/scoped_pipe.h

diff --git a/gdb/Makefile.in b/gdb/Makefile.in
index 0c331af4bf..458863b01c 100644
--- a/gdb/Makefile.in
+++ b/gdb/Makefile.in
@@ -447,6 +447,7 @@ SELFTESTS_SRCS = \
 	unittests/rsp-low-selftests.c \
 	unittests/scoped_fd-selftests.c \
 	unittests/scoped_mmap-selftests.c \
+	unittests/scoped_pipe-selftests.c \
 	unittests/scoped_restore-selftests.c \
 	unittests/string_view-selftests.c \
 	unittests/style-selftests.c \
diff --git a/gdb/unittests/scoped_pipe-selftests.c b/gdb/unittests/scoped_pipe-selftests.c
new file mode 100644
index 0000000000..14abbb1b7b
--- /dev/null
+++ b/gdb/unittests/scoped_pipe-selftests.c
@@ -0,0 +1,95 @@
+/* Self tests for scoped_pipe for GDB, the GNU debugger.
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#include "defs.h"
+
+#include "gdbsupport/scoped_pipe.h"
+#include "gdbsupport/selftest.h"
+
+namespace selftests {
+namespace scoped_pipe {
+
+/* Test that the pipe is correctly created.  */
+
+static void
+test_create ()
+{
+  ::scoped_pipe spipe;
+
+  SELF_CHECK (spipe.get_read_end () > 0);
+  SELF_CHECK (spipe.get_write_end () > 0);
+}
+
+/* Test that we can write and read from the pipe.  */
+
+static void
+test_transmission ()
+{
+  int foo = 123;
+  ::scoped_pipe spipe;
+
+  /* Write to the pipe.  */
+  {
+    ssize_t writeret;
+
+    do
+      {
+	writeret = write (spipe.get_write_end (), &foo, sizeof (foo));
+      }
+    while (writeret < 0 && (errno == EAGAIN || errno == EINTR));
+
+    SELF_CHECK (writeret > 0);
+  }
+
+  /* Read from the pipe, and check if the value read is the same as
+     the one that was written.  */
+  {
+    ssize_t readret;
+    int read_foo;
+
+    do
+      {
+	readret = read (spipe.get_read_end (), &read_foo, sizeof (read_foo));
+      }
+    while (readret < 0 && (errno == EAGAIN || errno == EINTR));
+
+    SELF_CHECK (readret > 0);
+
+    SELF_CHECK (read_foo == foo);
+  }
+}
+
+/* Run selftests.  */
+static void
+run_tests ()
+{
+  test_create ();
+  test_transmission ();
+}
+
+} /* namespace scoped_pipe */
+} /* namespace selftests */
+
+void _initialize_scoped_pipe_selftests ();
+void
+_initialize_scoped_pipe_selftests ()
+{
+  selftests::register_test ("scoped_pipe",
+			    selftests::scoped_pipe::run_tests);
+}
diff --git a/gdbsupport/scoped_pipe.h b/gdbsupport/scoped_pipe.h
new file mode 100644
index 0000000000..5180df80ae
--- /dev/null
+++ b/gdbsupport/scoped_pipe.h
@@ -0,0 +1,63 @@
+/* scoped_pipe, automatically close a pipe.
+
+   Copyright (C) 2020 Free Software Foundation, Inc.
+
+   This file is part of GDB.
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+#ifndef GDBSUPPORT_SCOPED_PIPE_H
+#define GDBSUPPORT_SCOPED_PIPE_H
+
+#include <unistd.h>
+#include "filestuff.h"
+
+/* A smart-pointer-like class to automatically close a pipe.  */
+
+class scoped_pipe
+{
+public:
+  scoped_pipe ()
+  {
+    if (gdb_pipe_cloexec (m_pipe) < 0)
+      error (_("gdb_pipe_cloexec: %s"), safe_strerror (errno));
+  }
+
+  ~scoped_pipe ()
+  {
+    if (m_pipe[0] >= 0)
+      close (m_pipe[0]);
+    if (m_pipe[1] >= 0)
+      close (m_pipe[1]);
+  }
+
+  DISABLE_COPY_AND_ASSIGN (scoped_pipe);
+
+  /* Get the read end of the pipe.  */
+  int get_read_end () const noexcept
+  {
+    return m_pipe[0];
+  }
+
+  /* Get the write end of the pipe.  */
+  int get_write_end () const noexcept
+  {
+    return m_pipe[1];
+  }
+
+private:
+  int m_pipe[2];
+};
+
+#endif /* ! GDBSUPPORT_SCOPED_PIPE_H */
-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
  2020-03-17 15:47       ` [PATCH v2 1/5] Introduce scoped_pipe.h Sergio Durigan Junior
@ 2020-03-17 15:47       ` Sergio Durigan Junior
  2020-03-27 18:20         ` Pedro Alves
  2020-03-17 15:47       ` [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
                         ` (3 subsequent siblings)
  5 siblings, 1 reply; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

Even though this patch is not necessarily needed by the main feature,
I think it's a good idea to have it.

We currently have this code on 'throw_perror_with_name':

  /* I understand setting these is a matter of taste.  Still, some people
     may clear errno but not know about bfd_error.  Doing this here is not
     unreasonable.  */
  bfd_set_error (bfd_error_no_error);
  errno = 0;

git blame tells me that this piece of code is pretty old; the commit
that "introduced" it is:

  commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc
  Author: Stan Shebs <shebs@codesourcery.com>
  Date:   Fri Apr 16 01:35:26 1999 +0000

      Initial creation of sourceware repository

so yeah...

If we go to the POSIX specification for 'perror', it doesn't really
say anything about whether errno should be preserved or not.  It does,
however, say that 'perror's messages should be the same as those
returned by 'strerror', and 'strerror' is not supposed to alter errno
if the call is successful.

Maybe when our wrapper was written it was OK to modify errno, I don't
know.  But I'd like to propose that we stick to POSIX in this case.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* utils.c (throw_perror_with_name): Don't reset
	errno/bfd_error.
---
 gdb/utils.c | 6 ------
 1 file changed, 6 deletions(-)

diff --git a/gdb/utils.c b/gdb/utils.c
index 0b470120a2..df8add1afa 100644
--- a/gdb/utils.c
+++ b/gdb/utils.c
@@ -595,12 +595,6 @@ throw_perror_with_name (enum errors errcode, const char *string)
 {
   std::string combined = perror_string (string);
 
-  /* I understand setting these is a matter of taste.  Still, some people
-     may clear errno but not know about bfd_error.  Doing this here is not
-     unreasonable.  */
-  bfd_set_error (bfd_error_no_error);
-  errno = 0;
-
   throw_error (errcode, _("%s."), combined.c_str ());
 }
 
-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
  2020-03-17 15:47       ` [PATCH v2 1/5] Introduce scoped_pipe.h Sergio Durigan Junior
  2020-03-17 15:47       ` [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
@ 2020-03-17 15:47       ` Sergio Durigan Junior
  2020-03-27  4:14         ` Kevin Buettner
  2020-03-27 13:06         ` Pedro Alves
  2020-03-17 15:47       ` [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
                         ` (2 subsequent siblings)
  5 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

This patch is one important piece of the series.  It expands
'fork_inferior' in order to deal with new steps in the process of
initializing the inferior.  We now have to:

- Create a pipe that will be used to communicate with our
  fork (pre-exec), and which the fork will use to pass back to us the
  errno value of the 'traceme_fun' call.

- Close this pipe after it is used.

- Check the errno value passed back from the fork, and report any
  problems in the initialization to the user.

I thought about and implemented a few designs for all of this, but
ended up sticking with the function overload one.  'fork_inferior' is
now two functions: one that will take a traceme function like
'(*traceme_fun) ()' --- i.e., the original 'fork_inferior' behaviour,
and other that will take a function like '(*traceme_fun) (int
trace_pipe_write)'.  Depending on which function it takes, we know
whether the user does not want us to check whether the 'traceme_fun'
call was successful (former) or if she does (latter).

All in all, the patch is not complicated to understand and keeps the
interface clean enough so that we don't have to update every caller of
'fork_inferior' (which was a problem with previous designs I tried).

The subsequent patch will build on top of this one and implement the
errno-passing-via-pipe on the GNU/Linux target.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	 * nat/fork-inferior.c: Include "gdbsupport/scoped_pipe.h".
	 (default_trace_me_fail_reason): New function.
	 (trace_me_fail_reason): New variable.
	 (write_trace_errno_to_pipe): New function.
	 (read_trace_errno_from_pipe): Likewise.
	 (check_child_trace_me_errno): Likewise.
	 (traceme_info): New struct.
	 (fork_inferior_1): Renamed from 'fork_inferior'.
	 (fork_inferior): New overloads.
	 (trace_start_error_with_name): Add "append" parameter.
	 * nat/fork-inferior.h (fork_inferior): Expand comment.
	 Add overload declaration.
	 (trace_start_error_with_name): Add "append" parameter.
	 (trace_me_fail_reason): New variable.
	 (check_child_trace_me_errno): New function.
	 (write_trace_errno_to_pipe): Likewise.
---
 gdb/nat/fork-inferior.c | 231 ++++++++++++++++++++++++++++++++++++----
 gdb/nat/fork-inferior.h |  87 ++++++++++++---
 2 files changed, 288 insertions(+), 30 deletions(-)

diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 1185ef8998..223ff44195 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -27,6 +27,7 @@
 #include "gdbsupport/pathstuff.h"
 #include "gdbsupport/signals-state-save-restore.h"
 #include "gdbsupport/gdb_tilde_expand.h"
+#include "gdbsupport/scoped_pipe.h"
 #include <vector>
 
 extern char **environ;
@@ -262,16 +263,157 @@ execv_argv::init_for_shell (const char *exec_file,
   m_argv.push_back (NULL);
 }
 
-/* See nat/fork-inferior.h.  */
+/* Default implementation of 'trace_me_fail_reason'.  Always return
+   an empty string.  */
 
-pid_t
-fork_inferior (const char *exec_file_arg, const std::string &allargs,
-	       char **env, void (*traceme_fun) (),
-	       gdb::function_view<void (int)> init_trace_fun,
-	       void (*pre_trace_fun) (),
-	       const char *shell_file_arg,
-               void (*exec_fun)(const char *file, char * const *argv,
-                                char * const *env))
+static std::string
+default_trace_me_fail_reason (int err)
+{
+  return {};
+}
+
+/* See fork-inferior.h.  */
+
+std::string (*trace_me_fail_reason) (int err)
+  = default_trace_me_fail_reason;
+
+/* See fork-inferior.h.  */
+
+void
+write_trace_errno_to_pipe (int writepipe, int trace_errno)
+{
+  ssize_t writeret;
+
+  do
+    {
+      writeret = write (writepipe, &trace_errno, sizeof (trace_errno));
+    }
+  while (writeret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (writeret < 0)
+    error (_("Could not write trace errno: %s"), safe_strerror (errno));
+}
+
+/* Helper function to read TRACE_ERRNO from READPIPE, which handles
+   EINTR/EAGAIN and throws and exception if there was an error.  */
+
+static int
+read_trace_errno_from_pipe (int readpipe)
+{
+  ssize_t readret;
+  int trace_errno;
+
+  do
+    {
+      readret = read (readpipe, &trace_errno, sizeof (trace_errno));
+    }
+  while (readret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (readret < 0)
+    error (_("Could not read trace errno: %s"), safe_strerror (errno));
+
+  return trace_errno;
+}
+
+/* See fork-inferior.h.  */
+
+void
+check_child_trace_me_errno (int readpipe)
+{
+  fd_set rset;
+  struct timeval timeout;
+  int ret;
+
+  /* Make sure we have a valid 'trace_me_fail_reason' function
+     defined.  */
+  gdb_assert (trace_me_fail_reason != nullptr);
+
+  FD_ZERO (&rset);
+  FD_SET (readpipe, &rset);
+
+  /* Five seconds should be plenty of time to wait for the child's
+     reply.  */
+  timeout.tv_sec = 5;
+  timeout.tv_usec = 0;
+
+  do
+    {
+      ret = select (readpipe + 1, &rset, NULL, NULL, &timeout);
+    }
+  while (ret < 0 && (errno == EAGAIN || errno == EINTR));
+
+  if (ret < 0)
+    perror_with_name ("select");
+  else if (ret == 0)
+    error (_("Timeout while waiting for child's trace errno"));
+  else
+    {
+      int child_errno;
+
+      child_errno = read_trace_errno_from_pipe (readpipe);
+
+      if (child_errno != 0)
+	{
+	  /* The child can't use TRACE_TRACEME.  We have to check whether
+	     we know the reason for the failure, and then error out.  */
+	  std::string reason = trace_me_fail_reason (child_errno);
+
+	  if (reason.empty ())
+	    reason = "Could not determine reason for trace failure.";
+
+	  /* The child is supposed to display a warning containing the
+	     safe_strerror message before us, so we just display the
+	     possible reason for the failure.  */
+	  error ("%s", reason.c_str ());
+	}
+    }
+}
+
+/* Helper struct for fork_inferior_1, containing information on
+   whether we should check if TRACEME_FUN was successfully called or
+   not.  */
+
+struct traceme_info
+{
+  /* True if we should check whether the call to 'traceme_fun
+     (TRACE_ME...)' succeeded or not. */
+  bool check_trace_me_fail_reason;
+
+  union
+  {
+    /* The non-check version of TRACEME_FUN.  It will be set if
+       CHECK_TRACEME_FAIL_REASON is false.
+
+       This function will usually just perform the call to whatever
+       trace function needed to start tracing the inferior (e.g.,
+       ptrace).  */
+    void (*traceme_fun_nocheck) ();
+
+    /* The check version of TRACEME_FUN.  It will be set if
+       CHECK_TRACEME_FAIL_REASON is true.
+
+       This function will usually perform the call to whatever trace
+       function needed to start tracing the inferior, but will also
+       write its errno value to TRACE_ERRNO_PIPE, so that
+       fork_inferior_1 can check whether it suceeded.  */
+    void (*traceme_fun_check) (int trace_errno_pipe);
+  } u;
+};
+
+/* Helper function.
+
+   Depending on the value of TRACEME_INFO.CHECK_TRACEME_FAIL_REASON,
+   this function will check whether the call to TRACEME_FUN succeeded
+   or not.  */
+
+static pid_t
+fork_inferior_1 (const char *exec_file_arg, const std::string &allargs,
+		 char **env, const struct traceme_info traceme_info,
+		 gdb::function_view<void (int)> init_trace_fun,
+		 void (*pre_trace_fun) (),
+		 const char *shell_file_arg,
+		 void (*exec_fun)(const char *file, char * const *argv,
+				  char * const *env))
 {
   pid_t pid;
   /* Set debug_fork then attach to the child while it sleeps, to debug.  */
@@ -283,6 +425,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   int save_errno;
   const char *inferior_cwd;
   std::string expanded_inferior_cwd;
+  scoped_pipe trace_pipe;
 
   /* If no exec file handed to us, get it from the exec-file command
      -- with a good, common error message if none is specified.  */
@@ -365,12 +508,6 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
   if (pid == 0)
     {
-      /* Close all file descriptors except those that gdb inherited
-	 (usually 0/1/2), so they don't leak to the inferior.  Note
-	 that this closes the file descriptors of all secondary
-	 UIs.  */
-      close_most_fds ();
-
       /* Change to the requested working directory if the user
 	 requested it.  */
       if (inferior_cwd != NULL)
@@ -392,7 +529,10 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
          for the inferior.  */
 
       /* "Trace me, Dr. Memory!"  */
-      (*traceme_fun) ();
+      if (traceme_info.check_trace_me_fail_reason)
+	(*traceme_info.u.traceme_fun_check) (trace_pipe.get_write_end ());
+      else
+	(*traceme_info.u.traceme_fun_nocheck) ();
 
       /* The call above set this process (the "child") as debuggable
         by the original gdb process (the "parent").  Since processes
@@ -403,6 +543,12 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
         saying "not parent".  Sorry; you'll have to use print
         statements!  */
 
+      /* Close all file descriptors except those that gdb inherited
+	 (usually 0/1/2), so they don't leak to the inferior.  Note
+	 that this closes the file descriptors of all secondary
+	 UIs, and the trace errno pipe (if it's open).  */
+      close_most_fds ();
+
       restore_original_signals_state ();
 
       /* There is no execlpe call, so we have to set the environment
@@ -431,6 +577,13 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
       _exit (0177);
     }
 
+  if (traceme_info.check_trace_me_fail_reason)
+    {
+      /* Check the trace errno, and inform the user about the reason
+	 of the failure, if there was any.  */
+      check_child_trace_me_errno (trace_pipe.get_read_end ());
+    }
+
   /* Restore our environment in case a vforked child clob'd it.  */
   environ = save_our_env;
 
@@ -448,6 +601,48 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
   return pid;
 }
 
+/* See fork-inferior.h.  */
+
+pid_t
+fork_inferior (const char *exec_file_arg, const std::string &allargs,
+	       char **env, void (*traceme_fun) (),
+	       gdb::function_view<void (int)> init_trace_fun,
+	       void (*pre_trace_fun) (),
+	       const char *shell_file_arg,
+               void (*exec_fun)(const char *file, char * const *argv,
+                                char * const *env))
+{
+  struct traceme_info traceme_info;
+
+  traceme_info.check_trace_me_fail_reason = false;
+  traceme_info.u.traceme_fun_nocheck = traceme_fun;
+
+  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
+			  init_trace_fun, pre_trace_fun, shell_file_arg,
+			  exec_fun);
+}
+
+/* See fork-inferior.h.  */
+
+pid_t
+fork_inferior (const char *exec_file_arg, const std::string &allargs,
+	       char **env, void (*traceme_fun) (int trace_errno_pipe),
+	       gdb::function_view<void (int)> init_trace_fun,
+	       void (*pre_trace_fun) (),
+	       const char *shell_file_arg,
+               void (*exec_fun)(const char *file, char * const *argv,
+                                char * const *env))
+{
+  struct traceme_info traceme_info;
+
+  traceme_info.check_trace_me_fail_reason = true;
+  traceme_info.u.traceme_fun_check = traceme_fun;
+
+  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
+			  init_trace_fun, pre_trace_fun, shell_file_arg,
+			  exec_fun);
+}
+
 /* See nat/fork-inferior.h.  */
 
 ptid_t
@@ -592,7 +787,7 @@ trace_start_error (const char *fmt, ...)
 /* See nat/fork-inferior.h.  */
 
 void
-trace_start_error_with_name (const char *string)
+trace_start_error_with_name (const char *string, const char *append)
 {
-  trace_start_error ("%s: %s", string, safe_strerror (errno));
+  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
 }
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index cf6f137edd..b67215353f 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -32,17 +32,41 @@ struct process_stratum_target;
 #define START_INFERIOR_TRAPS_EXPECTED 1
 
 /* Start an inferior Unix child process and sets inferior_ptid to its
-   pid.  EXEC_FILE is the file to run.  ALLARGS is a string containing
-   the arguments to the program.  ENV is the environment vector to
-   pass.  SHELL_FILE is the shell file, or NULL if we should pick
-   one.  EXEC_FUN is the exec(2) function to use, or NULL for the default
-   one.  */
-
-/* This function is NOT reentrant.  Some of the variables have been
-   made static to ensure that they survive the vfork call.  */
+   pid.
+
+   EXEC_FILE is the file to run.
+
+   ALLARGS is a string containing the arguments to the program.
+
+   ENV is the environment vector to pass.
+
+   SHELL_FILE is the shell file, or NULL if we should pick one.
+
+   EXEC_FUN is the exec(2) function to use, or NULL for the default
+   one.
+
+   This function is NOT reentrant.  Some of the variables have been
+   made static to ensure that they survive the vfork call.
+
+   This function does not check whether the call to TRACEME_FUN
+   succeeded or not.  */
 extern pid_t fork_inferior (const char *exec_file_arg,
 			    const std::string &allargs,
-			    char **env, void (*traceme_fun) (),
+			    char **env,
+			    void (*traceme_fun) (),
+			    gdb::function_view<void (int)> init_trace_fun,
+			    void (*pre_trace_fun) (),
+			    const char *shell_file_arg,
+			    void (*exec_fun) (const char *file,
+					      char * const *argv,
+					      char * const *env));
+
+/* Like fork_inferior above, but check whether the call to TRACEME_FUN
+   succeeded or not.  */
+extern pid_t fork_inferior (const char *exec_file_arg,
+			    const std::string &allargs,
+			    char **env,
+			    void (*traceme_fun) (int trace_errno_pipe),
 			    gdb::function_view<void (int)> init_trace_fun,
 			    void (*pre_trace_fun) (),
 			    const char *shell_file_arg,
@@ -82,9 +106,48 @@ extern void trace_start_error (const char *fmt, ...)
   ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
 
 /* Like "trace_start_error", but the error message is constructed by
-   combining STRING with the system error message for errno.  This
-   function does not return.  */
-extern void trace_start_error_with_name (const char *string)
+   combining STRING with the system error message for errno, and
+   (optionally) with APPEND.  This function does not return.  */
+extern void trace_start_error_with_name (const char *string,
+					 const char *append = "")
   ATTRIBUTE_NORETURN;
 
+/* Pointer to function which can be called by
+   'check_child_trace_me_errno' when we need to determine the reason
+   of a e.g. 'ptrace (PTRACE_ME, ...)' failure.  ERR is the ERRNO
+   value set by the failing ptrace call.
+
+   By default, the function returns an empty string (see
+   fork-inferior.c).
+
+   This pointer can be overriden by targets that want to personalize
+   the error message printed when trace fails (see linux-nat.c or
+   gdbserver/linux-low.c, for example).  */
+extern std::string (*trace_me_fail_reason) (int err);
+
+/* Check the "trace me" errno (generated when executing e.g. 'ptrace
+   (PTRACE_ME, ...)') of the child process that was created by
+   GDB/GDBserver when creating an inferior.  The errno value will be
+   passed via a pipe (see 'fork_inferior'), and READPIPE is the read
+   end of the pipe.
+
+   If possible (i.e., if 'trace_me_fail_reason' is defined by the
+   target), then we also try to determine the possible reason for a
+   failure.
+
+   The idea is to wait a few seconds (via 'select') until something is
+   written on READPIPE.  When that happens, we check if the child's
+   trace errno is different than 0.  If it is, we call the function
+   'trace_me_fail_reason' and try to obtain the reason for the
+   failure, and then throw an exception (with the reason as the
+   exception's message).
+
+   If nothing is written on the pipe, or if 'select' fails, we also
+   throw exceptions.  */
+extern void check_child_trace_me_errno (int readpipe);
+
+/* Helper function to write TRACE_ERRNO to WRITEPIPE, which handles
+   EINTR/EAGAIN and throws an exception if there was an error.  */
+extern void write_trace_errno_to_pipe (int writepipe, int trace_errno);
+
 #endif /* NAT_FORK_INFERIOR_H */
-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
                         ` (2 preceding siblings ...)
  2020-03-17 15:47       ` [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
@ 2020-03-17 15:47       ` Sergio Durigan Junior
  2020-03-27 15:28         ` Pedro Alves
  2020-03-27 17:02         ` Kevin Buettner
  2020-03-17 15:47       ` [PATCH v2 5/5] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
  2020-03-20  0:53       ` [PATCH v2 0/5] Improve ptrace-error detection Kevin Buettner
  5 siblings, 2 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

This patch implements the ptrace-errno-checking on the GNU/Linux
target (both native and remote).  It builds on top of the previous
'fork_inferior' extension patch.

The idea is to provide a new 'traceme_fun' for each ptrace backend,
which will accept a new integer argument representing the write end of
the ptrace status pipe (that was created in 'fork_inferior').  This
function will invoke the actual tracing syscall (which is 'ptrace' in
this case), get its errno value and write it back via the pipe.  You
can see examples of this new approach by looking at
'inf_ptrace_me' (GDB) or 'linux_ptrace_fun' (gdbserver).

The rest of the patch implements the necessary machinery to do
something useful with the errno information that we received from
'ptrace'.

In Fedora GDB, we carry the following patch:

  https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch

Its purpose is to try to detect a specific scenario where SELinux's
'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
order to debug the inferior (PTRACE_ATTACH and PTRACE_TRACEME will
fail with EACCES in this case).

I like the idea of improving error detection and providing more
information to the user (a simple "Permission denied" can be really
frustrating), but I don't fully agree with the way the patch was
implemented: it makes GDB link against libselinux only for the sake of
consulting the 'deny_ptrace' setting, and then prints a warning if
ptrace failed and this setting is on.

There is now a new function, 'linux_ptrace_restricted_fail_reason',
which does a few things to check what's wrong with ptrace:

  - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
    option is enabled.

  - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
    checks if it's different than 0.

For each of these checks, if it succeeds, the user will see a message
informing about the restriction in place, and how it can be disabled.
For example, if "deny_ptrace" is enabled, the user will see:

  # gdb /usr/bin/true
  ...
  (gdb) run
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Permission denied

  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) must
  be disabled in the target system (e.g., where GDBserver is running).

In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:

  # gdb /usr/bin/true
  ...
  (gdb) run
  Starting program: /usr/bin/true
  warning: Could not trace the inferior process.
  warning: ptrace: Operation not permitted

  The Linux kernel's Yama ptrace scope is in effect, which can prevent
  GDB from using 'ptrace'.  You can disable it by executing (as root):

    echo 0 > /proc/sys/kernel/yama/ptrace_scope

  If you are debugging the inferior remotely, the ptrace restriction(s) must
  be disabled in the target system (e.g., where GDBserver is running).

If both restrictions are enabled, both messages will show up.

This works for gdbserver as well, and actually fixes a latent bug I
found: when ptrace is restricted, gdbserver would hang due to an
unchecked ptrace call:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
  [ Here you would have to issue a C-c ]

Now, you will see:

  # gdbserver :9988 /usr/bin/true
  gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Permission denied
  gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
  gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2766868 No such process
  gdbserver: Could not trace the inferior process.
  gdbserver: ptrace: Permission denied

  The SELinux 'deny_ptrace' option is enabled and preventing GDB
  from using 'ptrace'.  You can disable it by executing (as root):

    setsebool deny_ptrace off

  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).
  Exiting.
  #

(I decided to keep all the other messages, even though I find them a
bit distracting).

If GDB can't determine the cause for the failure, it will still print
the generic error message which tells the user to check our
documentation:

  There might be restrictions preventing ptrace from working.  Please see
  the appendix "Linux kernel ptrace restrictions" in the GDB documentation
  for more details.
  If you are debugging the inferior remotely, the ptrace restriction(s) need
  to be disabled in the target system (e.g., where GDBserver is running).

This means that the series expands our documentation (in the next
patch) and creates a new appendix section named "Linux kernel ptrace
restrictions", with sub-sections for each possible restriction that
might be in place.

Notice how, on every message, we instruct the user to "do the right
thing" if gdbserver is being used.  This is because if the user
started gdbserver *before* any ptrace restriction was in place, and
then, for some reason, one or more restrictions get enabled, then the
error message will be displayed both on gdbserver *and* on the
connected GDB.  Since the user will be piloting GDB, it's important to
explicitly say that the ptrace restrictions are enabled in the target,
where gdbserver is running.

The current list of possible restrictions is:

  - SELinux's 'deny_ptrace' option (detected).

  - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).

  - seccomp on Docker containers (I couldn't find how to detect).

It's important to mention that all of this is Linux-specific; as far
as I know, SELinux, YAMA and seccomp are Linux-only features.

gdb/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* inf-ptrace.c: Include "nat/fork-inferior.h".
	(inf_ptrace_me): New parameter "trace_errno_wpipe".  Check
	"ptrace" errno.
	(inf_ptrace_target::attach): Rewrite to use
	"inf_ptrace_target::ptrace_attach".
	(inf_ptrace_target::ptrace_attach): New function, almost
	identical to the previous "inf_ptrace_target::attach".
	* inf-ptrace.h (struct inf_ptrace_target) <int ptrace_attach>:
	New method.
	* linux-nat.c: Include "nat/fork-inferior.h".
	(attach_proc_task_lwp_callback): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
	(linux_nat_target::attach): Save "ERRNO".  Pass it to
	"linux_ptrace_attach_fail_reason".
	(_initialize_linux_nat): Set "trace_me_fail_reason".
	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
	"nat/fork-inferior.h" and "gdbsupport/filestuff.h".
	(selinux_ftype): New type.
	(linux_ptrace_restricted_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_1): New function, renamed
	from "linux_ptrace_attach_fail_reason".
	(linux_ptrace_attach_fail_reason): New function.
	(linux_ptrace_attach_fail_reason_lwp): Likewise.
	(linux_ptrace_me_fail_reason): Likewise.
	(errno_pipe): New variable.
	(linux_child_function): Check "ptrace" errno.  Send it through
	the pipe.
	(linux_check_ptrace_features): Initialize pipe.  Check
	"ptrace" errno sent through the pipe.
	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): New
	function.
	(linux_ptrace_attach_fail_reason_lwp): Likewise.
	(linux_ptrace_me_fail_reason): Likewise.
	* remote.c (extended_remote_target::attach): Check error
	message on PACKET_ERROR.
	(remote_target::extended_remote_run): Likewise.

gdbserver/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* linux-low.cc (linux_ptrace_fun): New parameter
	"trace_errno_wpipe".  Check "ptrace" errno.
	(attach_proc_task_lwp_callback): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
	(linux_process_target::attach): Likewise.
	(initialize_low): Set "trace_me_fail_reason".
	* server.cc (handle_v_attach): Check if "attach_inferior"
	succeeded.
	(handle_v_run): Likewise.
	* thread-db.cc (attach_thread): Call
	"linux_ptrace_attach_fail_reason_lwp" instead of
	"linux_ptrace_attach_fail_reason_string".
---
 gdb/inf-ptrace.c        |  34 +++++++-
 gdb/inf-ptrace.h        |   2 +
 gdb/linux-nat.c         |  24 +++---
 gdb/nat/fork-inferior.c |   6 +-
 gdb/nat/fork-inferior.h |   2 +-
 gdb/nat/linux-ptrace.c  | 178 ++++++++++++++++++++++++++++++++++++++--
 gdb/nat/linux-ptrace.h  |  27 ++++--
 gdb/remote.c            |  40 ++++++++-
 gdbserver/linux-low.cc  |  31 +++++--
 gdbserver/server.cc     |  38 ++++++++-
 gdbserver/thread-db.cc  |   2 +-
 11 files changed, 339 insertions(+), 45 deletions(-)

diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
index db17a76d94..941f019709 100644
--- a/gdb/inf-ptrace.c
+++ b/gdb/inf-ptrace.c
@@ -34,6 +34,7 @@
 #include "nat/fork-inferior.h"
 #include "utils.h"
 #include "gdbarch.h"
+#include "nat/fork-inferior.h"
 
 \f
 
@@ -97,10 +98,23 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
 /* Prepare to be traced.  */
 
 static void
-inf_ptrace_me (void)
+inf_ptrace_me (int trace_errno_wpipe)
 {
   /* "Trace me, Dr. Memory!"  */
-  if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
+  int ret = ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  try
+    {
+      write_trace_errno_to_pipe (trace_errno_wpipe, ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  if (ret < 0)
     trace_start_error_with_name ("ptrace");
 }
 
@@ -185,6 +199,18 @@ inf_ptrace_target::mourn_inferior ()
 
 void
 inf_ptrace_target::attach (const char *args, int from_tty)
+{
+   errno = ptrace_attach (args, from_tty);
+   if (errno != 0)
+     perror_with_name (("ptrace"));
+}
+
+/* Attach to the process specified by ARGS.  If FROM_TTY is non-zero,
+   be chatty about it.  Return ERRNO if the call to ptrace failed; 0
+   otherwise.  */
+
+int
+inf_ptrace_target::ptrace_attach (const char *args, int from_tty)
 {
   pid_t pid;
   struct inferior *inf;
@@ -223,7 +249,7 @@ inf_ptrace_target::attach (const char *args, int from_tty)
   errno = 0;
   ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
   if (errno != 0)
-    perror_with_name (("ptrace"));
+    return errno;
 #else
   error (_("This system does not support attaching to a process"));
 #endif
@@ -241,6 +267,8 @@ inf_ptrace_target::attach (const char *args, int from_tty)
   set_executing (this, thr->ptid, true);
 
   unpusher.release ();
+
+  return 0;
 }
 
 #ifdef PT_GET_PROCESS_STATE
diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
index dd0733736f..ca36ca3af3 100644
--- a/gdb/inf-ptrace.h
+++ b/gdb/inf-ptrace.h
@@ -31,6 +31,8 @@ struct inf_ptrace_target : public inf_child_target
 
   void attach (const char *, int) override;
 
+  int ptrace_attach (const char *, int);
+
   void detach (inferior *inf, int) override;
 
   void resume (ptid_t, int, enum gdb_signal) override;
diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
index 81af83c4ac..cc044ee3ac 100644
--- a/gdb/linux-nat.c
+++ b/gdb/linux-nat.c
@@ -31,6 +31,7 @@
 #include "nat/linux-ptrace.h"
 #include "nat/linux-procfs.h"
 #include "nat/linux-personality.h"
+#include "nat/fork-inferior.h"
 #include "linux-fork.h"
 #include "gdbthread.h"
 #include "gdbcmd.h"
@@ -1136,7 +1137,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
 	  else
 	    {
 	      std::string reason
-		= linux_ptrace_attach_fail_reason_string (ptid, err);
+		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	      warning (_("Cannot attach to lwp %d: %s"),
 		       lwpid, reason.c_str ());
@@ -1185,20 +1186,15 @@ linux_nat_target::attach (const char *args, int from_tty)
   /* Make sure we report all signals during attach.  */
   pass_signals ({});
 
-  try
-    {
-      inf_ptrace_target::attach (args, from_tty);
-    }
-  catch (const gdb_exception_error &ex)
+  int err = inf_ptrace_target::ptrace_attach (args, from_tty);
+
+  if (err != 0)
     {
       pid_t pid = parse_pid_to_attach (args);
-      std::string reason = linux_ptrace_attach_fail_reason (pid);
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
 
-      if (!reason.empty ())
-	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
-		     ex.what ());
-      else
-	throw_error (ex.error, "%s", ex.what ());
+      error (_("warning: ptrace: %s\n%s"),
+	       safe_strerror (err), reason.c_str ());
     }
 
   /* The ptrace base target adds the main thread with (pid,0,0)
@@ -4582,6 +4578,10 @@ Enables printf debugging output."),
   sigemptyset (&blocked_mask);
 
   lwp_lwpid_htab_create ();
+
+  /* Set the proper function to generate a message when ptrace
+     fails.  */
+  trace_me_fail_reason = linux_ptrace_me_fail_reason;
 }
 \f
 
diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
index 223ff44195..eb4c4625d7 100644
--- a/gdb/nat/fork-inferior.c
+++ b/gdb/nat/fork-inferior.c
@@ -394,9 +394,9 @@ struct traceme_info
 
        This function will usually perform the call to whatever trace
        function needed to start tracing the inferior, but will also
-       write its errno value to TRACE_ERRNO_PIPE, so that
+       write its errno value to TRACE_ERRNO_WPIPE, so that
        fork_inferior_1 can check whether it suceeded.  */
-    void (*traceme_fun_check) (int trace_errno_pipe);
+    void (*traceme_fun_check) (int trace_errno_wpipe);
   } u;
 };
 
@@ -626,7 +626,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
 
 pid_t
 fork_inferior (const char *exec_file_arg, const std::string &allargs,
-	       char **env, void (*traceme_fun) (int trace_errno_pipe),
+	       char **env, void (*traceme_fun) (int trace_errno_wpipe),
 	       gdb::function_view<void (int)> init_trace_fun,
 	       void (*pre_trace_fun) (),
 	       const char *shell_file_arg,
diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
index b67215353f..3fbead2e33 100644
--- a/gdb/nat/fork-inferior.h
+++ b/gdb/nat/fork-inferior.h
@@ -66,7 +66,7 @@ extern pid_t fork_inferior (const char *exec_file_arg,
 extern pid_t fork_inferior (const char *exec_file_arg,
 			    const std::string &allargs,
 			    char **env,
-			    void (*traceme_fun) (int trace_errno_pipe),
+			    void (*traceme_fun) (int trace_errno_wpipe),
 			    gdb::function_view<void (int)> init_trace_fun,
 			    void (*pre_trace_fun) (),
 			    const char *shell_file_arg,
diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
index 5335d69092..b3fcf8bc07 100644
--- a/gdb/nat/linux-ptrace.c
+++ b/gdb/nat/linux-ptrace.c
@@ -21,6 +21,9 @@
 #include "linux-procfs.h"
 #include "linux-waitpid.h"
 #include "gdbsupport/buffer.h"
+#include "gdbsupport/gdb-dlfcn.h"
+#include "nat/fork-inferior.h"
+#include "gdbsupport/filestuff.h"
 #ifdef HAVE_SYS_PROCFS_H
 #include <sys/procfs.h>
 #endif
@@ -30,11 +33,93 @@
    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 *);
 
-std::string
-linux_ptrace_attach_fail_reason (pid_t pid)
+/* 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;
+
+  try
+    {
+      handle = gdb_dlopen ("libselinux.so.1");
+    }
+  catch (const gdb_exception_error &e)
+    {
+    }
+
+  if (handle != nullptr)
+    {
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  setsebool deny_ptrace off\n"));
+    }
+
+  gdb_file_up yama_ptrace_scope
+    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
+
+  if (yama_ptrace_scope != nullptr)
+    {
+      char yama_scope = fgetc (yama_ptrace_scope.get ());
+
+      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'.  You can disable it by executing (as root):\n\
+\n\
+  echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
+    }
+
+  if (ret.empty ())
+    {
+      /* It wasn't possible to determine the exact reason for the
+	 ptrace error.  Let's just emit a generic error message
+	 pointing the user to our documentation, where she can find
+	 instructions on how to try to diagnose the problem.  */
+      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.");
+    }
+
+  /* The user may be debugging remotely, so we have to warn that
+     the instructions above should be performed in the target.  */
+  string_appendf (ret,
+		  _("\n\
+If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
+be disabled in the target system (e.g., where GDBserver is running)."));
+
+  return ret;
+}
+
+/* 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.  Helper for linux_ptrace_attach_fail_reason and
+   linux_ptrace_attach_fail_reason_lwp.  */
+
+static std::string
+linux_ptrace_attach_fail_reason_1 (pid_t pid)
 {
   pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
   std::string result;
@@ -56,10 +141,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
 /* See linux-ptrace.h.  */
 
 std::string
-linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
+linux_ptrace_attach_fail_reason (pid_t pid, int err)
+{
+  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
+  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
+
+  if (!ptrace_restrict.empty ())
+    result += "\n" + ptrace_restrict;
+
+  return result;
+}
+
+/* See linux-ptrace.h.  */
+
+std::string
+linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
 {
   long lwpid = ptid.lwp ();
-  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
+  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
 
   if (!reason.empty ())
     return string_printf ("%s (%d), %s", safe_strerror (err), err,
@@ -68,6 +167,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.  */
@@ -257,6 +364,12 @@ linux_ptrace_test_ret_to_nx (void)
 #endif /* defined __i386__ || defined __x86_64__ */
 }
 
+/* If the PTRACE_TRACEME call on linux_child_function errors, we need
+   to be able to send ERRNO back to the parent so that it can check
+   whether there are restrictions in place preventing ptrace from
+   working.  We do that with a pipe.  */
+static int errno_pipe[2];
+
 /* Helper function to fork a process and make the child process call
    the function FUNCTION, passing CHILD_STACK as parameter.
 
@@ -321,7 +434,30 @@ 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);
+  /* Close read end.  */
+  close (errno_pipe[0]);
+
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  /* Write ERRNO to the pipe, even if it's zero, and close the writing
+     end of the pipe.  */
+  try
+    {
+      write_trace_errno_to_pipe (errno_pipe[1], ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  close (errno_pipe[1]);
+
+  if (ret != 0)
+    trace_start_error_with_name ("ptrace");
+
   kill (getpid (), SIGSTOP);
 
   /* Fork a grandchild.  */
@@ -346,12 +482,40 @@ linux_check_ptrace_features (void)
   /* Initialize the options.  */
   supported_ptrace_options = 0;
 
+  /* Initialize our pipe.  */
+  if (gdb_pipe_cloexec (errno_pipe) < 0)
+    perror_with_name ("gdb_pipe_cloexec");
+
   /* Fork a child so we can do some testing.  The child will call
      linux_child_function and will get traced.  The child will
      eventually fork a grandchild so we can test fork event
      reporting.  */
   child_pid = linux_fork_to_function (NULL, linux_child_function);
 
+  /* We don't need the write end of the pipe anymore.  */
+  close (errno_pipe[1]);
+
+  try
+    {
+      /* Check whether 'ptrace (PTRACE_ME, ...)' failed when being
+	 invoked by the child.  If it did, we might get the
+	 possible reason for it as the exception message.  */
+      check_child_trace_me_errno (errno_pipe[0]);
+    }
+  catch (const gdb_exception &e)
+    {
+      /* Close the pipe so we don't leak fd's.  */
+      close (errno_pipe[0]);
+
+      /* A failure here means that PTRACE_ME failed, which means that
+	 GDB/gdbserver will most probably not work correctly.  If we
+	 want to be pedantic, we could just call 'exit' here.
+	 However, let's just re-throw the exception.  */
+      throw;
+    }
+
+  close (errno_pipe[0]);
+
   ret = my_waitpid (child_pid, &status, 0);
   if (ret == -1)
     perror_with_name (("waitpid"));
diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
index 65568301f2..7cb77114ca 100644
--- a/gdb/nat/linux-ptrace.h
+++ b/gdb/nat/linux-ptrace.h
@@ -176,12 +176,27 @@ struct buffer;
 # define TRAP_HWBKPT 4
 #endif
 
-extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
-
-/* 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);
+/* 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);
+
+/* Find all possible reasons we could have failed to attach to PID's
+   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
+   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
+   this function should be used when attaching to an LWP other than
+   the leader; it does not warn about ptrace restrictions.  */
+extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
diff --git a/gdb/remote.c b/gdb/remote.c
index 0f78b1be1b..aacbdf1984 100644
--- a/gdb/remote.c
+++ b/gdb/remote.c
@@ -5882,9 +5882,26 @@ extended_remote_target::attach (const char *args, int from_tty)
       break;
     case PACKET_UNKNOWN:
       error (_("This target does not support attaching to a process"));
+    case PACKET_ERROR:
+      {
+	std::string errmsg = rs->buf.data ();
+
+	/* Check if we have a specific error (i.e., not a generic
+	   "E01") coming from the target.  If there is, we print it
+	   here.  */
+	if (startswith (errmsg.c_str (), "E."))
+	  {
+	    /* Get rid of the "E." prefix.  */
+	    errmsg.erase (0, 2);
+	  }
+
+	error (_("Attaching to %s failed%s%s"),
+	       target_pid_to_str (ptid_t (pid)).c_str (),
+	       !errmsg.empty () ? "\n" : "",
+	       errmsg.c_str ());
+      }
     default:
-      error (_("Attaching to %s failed"),
-	     target_pid_to_str (ptid_t (pid)).c_str ());
+      gdb_assert_not_reached (_("bad switch"));
     }
 
   set_current_inferior (remote_add_inferior (false, pid, 1, 0));
@@ -10024,8 +10041,23 @@ remote_target::extended_remote_run (const std::string &args)
 	error (_("Running the default executable on the remote target failed; "
 		 "try \"set remote exec-file\"?"));
       else
-	error (_("Running \"%s\" on the remote target failed"),
-	       remote_exec_file);
+	{
+	  std::string errmsg = rs->buf.data ();
+
+	  /* Check if we have a specific error (i.e., not a generic
+	     "E01") coming from the target.  If there is, we print it
+	     here.  */
+	  if (startswith (errmsg.c_str (), "E."))
+	    {
+	      /* Get rid of the "E." prefix.  */
+	      errmsg.erase (0, 2);
+	    }
+
+	  error (_("Running \"%s\" on the remote target failed%s%s"),
+		 remote_exec_file,
+		 !errmsg.empty () ? "\n" : "",
+		 errmsg.c_str ());
+	}
     default:
       gdb_assert_not_reached (_("bad switch"));
     }
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index 2872bc78da..42283802dd 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -968,10 +968,24 @@ add_lwp (ptid_t ptid)
    actually initiating the tracing of the inferior.  */
 
 static void
-linux_ptrace_fun ()
+linux_ptrace_fun (int ptrace_errno_wpipe)
 {
-  if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
-	      (PTRACE_TYPE_ARG4) 0) < 0)
+  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
+		    (PTRACE_TYPE_ARG4) 0);
+  int ptrace_errno = ret < 0 ? errno : 0;
+
+  try
+    {
+      write_trace_errno_to_pipe (ptrace_errno_wpipe, ptrace_errno);
+    }
+  catch (const gdb_exception &e)
+    {
+      warning ("%s", e.what ());
+      _exit (0177);
+    }
+
+  errno = ptrace_errno;
+  if (ret < 0)
     trace_start_error_with_name ("ptrace");
 
   if (setpgid (0, 0) < 0)
@@ -1170,7 +1184,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
       else if (err != 0)
 	{
 	  std::string reason
-	    = linux_ptrace_attach_fail_reason_string (ptid, err);
+	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
 	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
 	}
@@ -1202,8 +1216,8 @@ linux_process_target::attach (unsigned long pid)
     {
       remove_process (proc);
 
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
-      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
+      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
+      error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
     }
 
   /* Don't ignore the initial SIGSTOP if we just attached to this
@@ -7552,5 +7566,10 @@ initialize_low (void)
 
   initialize_low_arch ();
 
+  /* Initialize the 'trace_me_fail_reason' function pointer.  We will
+     use this to determine the reason for possible failures when
+     invoking 'ptrace (PTRACE_ME, ...)'.  */
+  trace_me_fail_reason = linux_ptrace_me_fail_reason;
+
   linux_check_ptrace_features ();
 }
diff --git a/gdbserver/server.cc b/gdbserver/server.cc
index 43962adc86..003385f42a 100644
--- a/gdbserver/server.cc
+++ b/gdbserver/server.cc
@@ -2892,9 +2892,31 @@ handle_v_attach (char *own_buf)
 {
   client_state &cs = get_client_state ();
   int pid;
+  int ret;
 
   pid = strtol (own_buf + 8, NULL, 16);
-  if (pid != 0 && attach_inferior (pid) == 0)
+
+  if (pid <= 0)
+    {
+      write_enn (own_buf);
+      return 0;
+    }
+
+  try
+    {
+      /* Attach to the specified PID.  This function can throw, so we
+	 make sure to catch the exception and send it (as an error
+	 packet) back to GDB.  */
+      ret = attach_inferior (pid);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      fprintf (stderr, "%s\n", e.what ());
+      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
+      return 0;
+    }
+
+  if (ret == 0)
     {
       /* Don't report shared library events after attaching, even if
 	 some libraries are preloaded.  GDB will always poll the
@@ -3030,7 +3052,19 @@ handle_v_run (char *own_buf)
   free_vector_argv (program_args);
   program_args = new_argv;
 
-  target_create_inferior (program_path.get (), program_args);
+  try
+    {
+      /* Create the inferior.  This function can throw, so we make
+	 sure to catch the exception and send it (as an error packet)
+	 back to GDB.  */
+      target_create_inferior (program_path.get (), program_args);
+    }
+  catch (const gdb_exception_error &e)
+    {
+      fprintf (stderr, "%s\n", e.what ());
+      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
+      return 0;
+    }
 
   if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
     {
diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc
index 2bb6d28820..60ceb7b663 100644
--- a/gdbserver/thread-db.cc
+++ b/gdbserver/thread-db.cc
@@ -224,7 +224,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
   err = linux_attach_lwp (ptid);
   if (err != 0)
     {
-      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
+      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
 
       warning ("Could not attach to thread %ld (LWP %d): %s",
 	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* [PATCH v2 5/5] Document Linux-specific possible ptrace restrictions
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
                         ` (3 preceding siblings ...)
  2020-03-17 15:47       ` [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
@ 2020-03-17 15:47       ` Sergio Durigan Junior
  2020-03-20  0:53       ` [PATCH v2 0/5] Improve ptrace-error detection Kevin Buettner
  5 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-17 15:47 UTC (permalink / raw)
  To: GDB Patches
  Cc: Pedro Alves, Tom Tromey, Kevin Buettner, Sergio Durigan Junior

This patch creates a new "Linux kernel ptrace restrictions" which
documents possible causes that can be prevent the inferior from being
correctly started/debugged.

This has been pre-approved by Eli.

gdb/doc/ChangeLog:
yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>

	* gdb.texinfo (Linux kernel ptrace restrictions): New appendix
	section.
---
 gdb/doc/gdb.texinfo | 136 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 136 insertions(+)

diff --git a/gdb/doc/gdb.texinfo b/gdb/doc/gdb.texinfo
index 385c832f22..0a6c6f6df6 100644
--- a/gdb/doc/gdb.texinfo
+++ b/gdb/doc/gdb.texinfo
@@ -182,6 +182,9 @@ software in general.  We will miss him.
                                 @value{GDBN}
 * Operating System Information:: Getting additional information from
                                  the operating system
+* Linux kernel ptrace restrictions::        Restrictions sometimes
+                                            imposed by the Linux
+                                            kernel on @code{ptrace}
 * Trace File Format::		GDB trace file format
 * Index Section Format::        .gdb_index section format
 * Man Pages::			Manual pages
@@ -45656,6 +45659,139 @@ should contain a comma-separated list of cores that this process
 is running on.  Target may provide additional columns,
 which @value{GDBN} currently ignores.
 
+@node Linux kernel ptrace restrictions
+@appendix Linux kernel @code{ptrace} restrictions
+@cindex linux kernel ptrace restrictions, attach
+
+The @code{ptrace} system call is used by @value{GDBN} and
+@code{gdbserver} on GNU/Linux to, among other things, attach to a new
+or existing inferior in order to start debugging it.  Due to security
+concerns, some distributions and vendors disable or severely restrict
+the ability to perform these operations, which can make @value{GDBN}
+or @code{gdbserver} malfunction.  In this section, we will expand on
+how this malfunction can manifest itself, and how to modify the
+system's settings in order to be able to use @value{GDBN} and
+@code{gdbserver} properly.
+
+@menu
+* The error message::                   The error message displayed when the
+                                        system prevents @value{GDBN}
+                                        or @code{gdbserver} from using
+                                        @code{ptrace}
+* SELinux's deny_ptrace::               SELinux and the @code{deny_ptrace} option
+* Yama's ptrace_scope::                 Yama and the @code{ptrace_scope} setting
+* Docker and seccomp::                  Docker and the @code{seccomp}
+                                        infrastructure
+@end menu
+
+@node The error message
+@appendixsection The error message
+
+When the system prevents @value{GDBN} or @code{gdbserver} from using
+the @code{ptrace} system call, you will likely see a descriptive error
+message explaining what is wrong and how to attempt to fix the
+problem.  For example, when SELinux's @code{deny_ptrace} option is
+enabled, you can see:
+
+@smallexample
+$ gdb program
+...
+(@value{GDBP}) run
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+The SELinux 'deny_ptrace' option is enabled and preventing @value{GDBN}
+from using 'ptrace'.  You can disable it by executing (as root):
+
+  setsebool deny_ptrace off
+
+If you are debugging the inferior remotely, the instruction(s) above must
+be performed in the target system (e.g., where GDBserver is running).
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+Sometimes, it may not be possible to acquire the necessary data to
+determine the root cause of the failure.  In this case, you will see a
+generic error message pointing you to this section:
+
+@smallexample
+$ gdb program
+...
+Starting program: program
+warning: Could not trace the inferior process.
+Error:
+warning: ptrace: Permission denied
+There might be restrictions preventing ptrace from working.  Please see
+the appendix "Linux kernel ptrace restrictions" in the GDB documentation
+for more details.
+During startup program exited with code 127.
+(@value{GDBP})
+@end smallexample
+
+@node SELinux's deny_ptrace
+@appendixsection SELinux's @code{deny_ptrace}
+@cindex SELinux
+@cindex deny_ptrace
+
+If you are using SELinux, you might want to check whether the
+@code{deny_ptrace} option is enabled by doing:
+
+@smallexample
+$ getsebool deny_ptrace
+deny_ptrace --> on
+@end smallexample
+
+If the option is enabled, you can disable it by doing, as root:
+
+@smallexample
+# setsebool deny_ptrace off
+@end smallexample
+
+The option will be disabled until the next reboot.  If you would like
+to disable it permanently, you can do (as root):
+
+@smallexample
+# setsebool -P deny_ptrace off
+@end smallexample
+
+@node Yama's ptrace_scope
+@appendixsection Yama's @code{ptrace_scope}
+@cindex yama, ptrace_scope
+
+If your system has Yama enabled, you might want to check whether the
+@code{ptrace_scope} setting is enabled by checking the value of
+@file{/proc/sys/kernel/yama/ptrace_scope}:
+
+@smallexample
+$ cat /proc/sys/kernel/yama/ptrace_scope
+0
+@end smallexample
+
+If you see anything other than @code{0}, @value{GDBN} or
+@code{gdbserver} can be affected by it.  You can temporarily disable
+the feature by doing, as root:
+
+@smallexample
+# sysctl -w kernel.yama.ptrace_scope=0
+kernel.yama.ptrace_scope = 0
+@end smallexample
+
+@node Docker and seccomp
+@appendixsection Docker and @code{seccomp}
+@cindex docker, seccomp
+
+If you are using Docker (@uref{https://www.docker.com/}) containers,
+you will probably have to disable its @code{seccomp} protections in
+order to be able to use @value{GDBN} or @code{gdbserver}.  To do that,
+you can use the options @code{--cap-add=SYS_PTRACE --security-opt
+seccomp=unconfined} when invoking Docker:
+
+@smallexample
+$ docker run --cap-add=SYS_PTRACE --security-opt seccomp=unconfined
+@end smallexample
+
 @node Trace File Format
 @appendix Trace File Format
 @cindex trace file format
-- 
2.24.1


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 0/5] Improve ptrace-error detection
  2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
                         ` (4 preceding siblings ...)
  2020-03-17 15:47       ` [PATCH v2 5/5] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
@ 2020-03-20  0:53       ` Kevin Buettner
  2020-03-24 18:23         ` Sergio Durigan Junior
  5 siblings, 1 reply; 98+ messages in thread
From: Kevin Buettner @ 2020-03-20  0:53 UTC (permalink / raw)
  To: gdb-patches

Hi Sergio,

I'm still reviewing this patch set, but noticed the following during testing:

< PASS: gdb.base/attach-twice.exp: attach
---
> XFAIL: gdb.base/attach-twice.exp: attach

and:

< PASS: gdb.base/attach.exp: do_attach_failure_tests: fail to attach again
---
> FAIL: gdb.base/attach.exp: do_attach_failure_tests: fail to attach again

For gdb.base/attach-twice.exp, the relevant sections of the log files look
like this:

(gdb) spawn /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice
attach 1113400
Attaching to program: /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice, process 1113400
warning: process 1113400 is already traced by process 1113405
ptrace: Operation not permitted.
(gdb) PASS: gdb.base/attach-twice.exp: attach

Versus:

(gdb) spawn /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice
attach 1113182
Attaching to program: /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice, process 1113182
warning: ptrace: Operation not permitted
process 1113182 is already traced by process 1113187

There might be restrictions preventing ptrace from working.  Please see
the appendix "Linux kernel ptrace restrictions" in the GDB documentation
for more details.
If you are debugging the inferior remotely, the ptrace restriction(s) must
be disabled in the target system (e.g., where GDBserver is running).
(gdb) XFAIL: gdb.base/attach-twice.exp: attach

It seems to me that this should still PASS; I think the regex for that
test simply needs to be updated.  (You could also add another case.)

I've also looked at the logs for gdb.base/attach.exp.  The output is
similar to that shown above.  Again, I think that the regex needs to
be updated.

If it's the case that the old output might still be produced by some
platforms, care must be taken to ensure that both cases (still) pass.

Kevin


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 0/5] Improve ptrace-error detection
  2020-03-20  0:53       ` [PATCH v2 0/5] Improve ptrace-error detection Kevin Buettner
@ 2020-03-24 18:23         ` Sergio Durigan Junior
  0 siblings, 0 replies; 98+ messages in thread
From: Sergio Durigan Junior @ 2020-03-24 18:23 UTC (permalink / raw)
  To: Kevin Buettner; +Cc: gdb-patches

On Thursday, March 19 2020, Kevin Buettner wrote:

> Hi Sergio,

Hey Kevin,

Thanks for the review.

> I'm still reviewing this patch set, but noticed the following during testing:
>
> < PASS: gdb.base/attach-twice.exp: attach
> ---
>> XFAIL: gdb.base/attach-twice.exp: attach
>
> and:
>
> < PASS: gdb.base/attach.exp: do_attach_failure_tests: fail to attach again
> ---
>> FAIL: gdb.base/attach.exp: do_attach_failure_tests: fail to attach again
>
> For gdb.base/attach-twice.exp, the relevant sections of the log files look
> like this:
>
> (gdb) spawn /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice
> attach 1113400
> Attaching to program: /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice, process 1113400
> warning: process 1113400 is already traced by process 1113405
> ptrace: Operation not permitted.
> (gdb) PASS: gdb.base/attach-twice.exp: attach
>
> Versus:
>
> (gdb) spawn /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice
> attach 1113182
> Attaching to program: /mesquite2/sourceware-git/f31-ptrace-error-detection/bld/gdb/testsuite/outputs/gdb.base/attach-twice/attach-twice, process 1113182
> warning: ptrace: Operation not permitted
> process 1113182 is already traced by process 1113187

> There might be restrictions preventing ptrace from working.  Please see
> the appendix "Linux kernel ptrace restrictions" in the GDB documentation
> for more details.
> If you are debugging the inferior remotely, the ptrace restriction(s) must
> be disabled in the target system (e.g., where GDBserver is running).
> (gdb) XFAIL: gdb.base/attach-twice.exp: attach

Ah, good catch.  I can reproduce these here (obviously), which makes me
wonder why I missed them.  I think I may have looked at them and thought
they were racy.

> It seems to me that this should still PASS; I think the regex for that
> test simply needs to be updated.  (You could also add another case.)

Yeah.

> I've also looked at the logs for gdb.base/attach.exp.  The output is
> similar to that shown above.  Again, I think that the regex needs to
> be updated.

You know, I thought it was just going to be a matter of expanding the
regexp in order to match the extra text, but then I started thinking if
there was a better way to do this.  I mean, in these two specific cases
(attach.exp and attach-twice.exp) we *know* what is wrong: we're trying
to attach twice to the same process.  So GDB knows this is the problem,
and when we print the whole "There might be restrictions preventing
ptrace from working..." text, it can confuse the user.

Looking at nat/linux-ptrace.c:linux_ptrace_attach_fail_reason, I see
that my patch is currently doing:

  std::string
  linux_ptrace_attach_fail_reason (pid_t pid, int err)
  {
    std::string result = linux_ptrace_attach_fail_reason_1 (pid);
    std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);

   if (!ptrace_restrict.empty ())
     result += "\n" + ptrace_restrict;

IOW, it's always appending the result of
"linux_ptrace_restricted_fail_reason" to the string that will be
printed.  Upon a closer inspection of
"linux_ptrace_attach_fail_reason_1"'s comment, we see:

  /* 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.  Helper for linux_ptrace_attach_fail_reason and
     linux_ptrace_attach_fail_reason_lwp.  */

  static std::string
  linux_ptrace_attach_fail_reason_1 (pid_t pid)

IOW, if the string returned by it is not empty, it means that the
function was able to determine the reason for the ptrace failure.

After seeing this, I decided that the best approach is to call
"linux_ptrace_restricted_fail_reason" only if
"linux_ptrace_attach_fail_reason_1" returns an empty string.

With this, the output generated when the user tries to attach twice to
the same process is kept minimal and concise.

It was still necessary to make a small adjustment in both testcases
because the order of the warnings was reversed: we now first print the
message saying that the process is already attached to GDB, and then
print the ptrace strerror string.

> If it's the case that the old output might still be produced by some
> platforms, care must be taken to ensure that both cases (still) pass.

Right.  I believe that with the change I described above it won't be
necessary to worry about arch-specific cases.

Thanks,

-- 
Sergio
GPG key ID: 237A 54B1 0287 28BF 00EF  31F4 D0EB 7628 65FC 5E36
Please send encrypted e-mail if possible
http://sergiodj.net/


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  2020-03-17 15:47       ` [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
@ 2020-03-27  4:14         ` Kevin Buettner
  2020-03-27 13:06         ` Pedro Alves
  1 sibling, 0 replies; 98+ messages in thread
From: Kevin Buettner @ 2020-03-27  4:14 UTC (permalink / raw)
  To: gdb-patches

Hi Sergio,

See my comments below.

On Tue, 17 Mar 2020 11:47:17 -0400
Sergio Durigan Junior <sergiodj@redhat.com> wrote:

> +      if (child_errno != 0)
> +	{
> +	  /* The child can't use TRACE_TRACEME.  We have to check whether
> +	     we know the reason for the failure, and then error out.  */
> +	  std::string reason = trace_me_fail_reason (child_errno);

I think you mean PTRACE_TRACEME in the comment, right?

[...]
> +/* Helper struct for fork_inferior_1, containing information on
> +   whether we should check if TRACEME_FUN was successfully called or
> +   not.  */
> +
> +struct traceme_info
> +{
> +  /* True if we should check whether the call to 'traceme_fun
> +     (TRACE_ME...)' succeeded or not. */
> +  bool check_trace_me_fail_reason;

While reading the patch, I noticed that some things have "traceme" or
"TRACEME" in the their names while others instead use "trace_me" or
"TRACE_ME".  While it's already the case that GDB is inconsistent in
this regard, I think it might be a good to not propogate that
inconsistency further.

It's likely that this came about due to the two differently named
constants used with ptrace(), i.e. PTRACE_TRACEME and PT_TRACE_ME. 
Since these constants are used on different platforms, I don't think
there's any way we can reconcile these.  But for, for names that we
pick, I think we should choose either "traceme" or "trace_me" and stick
with it.

I prefer "traceme" / "TRACEME", but won't object though if you choose
the other option.

> +
> +  union
> +  {
> +    /* The non-check version of TRACEME_FUN.  It will be set if
> +       CHECK_TRACEME_FAIL_REASON is false.

"CHECK_TRACEME_FAIL_REASON" in this comment doesn't match the
declaration above in which check_trace_me_fail_reason (with the _
between "trace" and "me") is used instead.  There are several
instances; I'll only point out this one.

[...]
> +/* Pointer to function which can be called by
> +   'check_child_trace_me_errno' when we need to determine the reason
> +   of a e.g. 'ptrace (PTRACE_ME, ...)' failure.  ERR is the ERRNO
> +   value set by the failing ptrace call.
> +
> +   By default, the function returns an empty string (see
> +   fork-inferior.c).
> +
> +   This pointer can be overriden by targets that want to personalize
> +   the error message printed when trace fails (see linux-nat.c or
> +   gdbserver/linux-low.c, for example).  */
> +extern std::string (*trace_me_fail_reason) (int err);

On my first pass through this patch, I had concerns about the
introduction of this global variable.  (That's why I asked for a
new set of patches.)  I can't think of a better way to do it though.

Kevin


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded
  2020-03-17 15:47       ` [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
  2020-03-27  4:14         ` Kevin Buettner
@ 2020-03-27 13:06         ` Pedro Alves
  1 sibling, 0 replies; 98+ messages in thread
From: Pedro Alves @ 2020-03-27 13:06 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey

On 3/17/20 3:47 PM, Sergio Durigan Junior via Gdb-patches wrote:
> This patch is one important piece of the series.  It expands
> 'fork_inferior' in order to deal with new steps in the process of
> initializing the inferior.  We now have to:
> 
> - Create a pipe that will be used to communicate with our
>   fork (pre-exec), and which the fork will use to pass back to us the
>   errno value of the 'traceme_fun' call.
> 
> - Close this pipe after it is used.
> 
> - Check the errno value passed back from the fork, and report any
>   problems in the initialization to the user.
> 
> I thought about and implemented a few designs for all of this, but
> ended up sticking with the function overload one.  'fork_inferior' is
> now two functions: one that will take a traceme function like
> '(*traceme_fun) ()' --- i.e., the original 'fork_inferior' behaviour,
> and other that will take a function like '(*traceme_fun) (int
> trace_pipe_write)'.  Depending on which function it takes, we know
> whether the user does not want us to check whether the 'traceme_fun'
> call was successful (former) or if she does (latter).

Pedantically: user -> caller.  she -> it.  "user" normally refers to
a person who uses gdb.

> All in all, the patch is not complicated to understand and keeps the
> interface clean enough so that we don't have to update every caller of
> 'fork_inferior' (which was a problem with previous designs I tried).
> 
> The subsequent patch will build on top of this one and implement the
> errno-passing-via-pipe on the GNU/Linux target.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	 * nat/fork-inferior.c: Include "gdbsupport/scoped_pipe.h".
> 	 (default_trace_me_fail_reason): New function.
> 	 (trace_me_fail_reason): New variable.
> 	 (write_trace_errno_to_pipe): New function.
> 	 (read_trace_errno_from_pipe): Likewise.
> 	 (check_child_trace_me_errno): Likewise.
> 	 (traceme_info): New struct.
> 	 (fork_inferior_1): Renamed from 'fork_inferior'.
> 	 (fork_inferior): New overloads.
> 	 (trace_start_error_with_name): Add "append" parameter.
> 	 * nat/fork-inferior.h (fork_inferior): Expand comment.
> 	 Add overload declaration.
> 	 (trace_start_error_with_name): Add "append" parameter.
> 	 (trace_me_fail_reason): New variable.
> 	 (check_child_trace_me_errno): New function.
> 	 (write_trace_errno_to_pipe): Likewise.
> ---
>  gdb/nat/fork-inferior.c | 231 ++++++++++++++++++++++++++++++++++++----
>  gdb/nat/fork-inferior.h |  87 ++++++++++++---
>  2 files changed, 288 insertions(+), 30 deletions(-)
> 
> diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
> index 1185ef8998..223ff44195 100644
> --- a/gdb/nat/fork-inferior.c
> +++ b/gdb/nat/fork-inferior.c
> @@ -27,6 +27,7 @@
>  #include "gdbsupport/pathstuff.h"
>  #include "gdbsupport/signals-state-save-restore.h"
>  #include "gdbsupport/gdb_tilde_expand.h"
> +#include "gdbsupport/scoped_pipe.h"
>  #include <vector>
>  
>  extern char **environ;
> @@ -262,16 +263,157 @@ execv_argv::init_for_shell (const char *exec_file,
>    m_argv.push_back (NULL);
>  }
>  
> -/* See nat/fork-inferior.h.  */
> +/* Default implementation of 'trace_me_fail_reason'.  Always return
> +   an empty string.  */
>  
> -pid_t
> -fork_inferior (const char *exec_file_arg, const std::string &allargs,
> -	       char **env, void (*traceme_fun) (),
> -	       gdb::function_view<void (int)> init_trace_fun,
> -	       void (*pre_trace_fun) (),
> -	       const char *shell_file_arg,
> -               void (*exec_fun)(const char *file, char * const *argv,
> -                                char * const *env))
> +static std::string
> +default_trace_me_fail_reason (int err)
> +{
> +  return {};
> +}
> +
> +/* See fork-inferior.h.  */
> +
> +std::string (*trace_me_fail_reason) (int err)
> +  = default_trace_me_fail_reason;
> +
> +/* See fork-inferior.h.  */
> +
> +void
> +write_trace_errno_to_pipe (int writepipe, int trace_errno)
> +{
> +  ssize_t writeret;
> +
> +  do
> +    {
> +      writeret = write (writepipe, &trace_errno, sizeof (trace_errno));
> +    }
> +  while (writeret < 0 && (errno == EAGAIN || errno == EINTR));
> +
> +  if (writeret < 0)
> +    error (_("Could not write trace errno: %s"), safe_strerror (errno));

I'll have a comment about this error in a following patch.


> +}
> +
> +/* Helper function to read TRACE_ERRNO from READPIPE, which handles
> +   EINTR/EAGAIN and throws and exception if there was an error.  */
> +
> +static int
> +read_trace_errno_from_pipe (int readpipe)
> +{
> +  ssize_t readret;
> +  int trace_errno;
> +
> +  do
> +    {
> +      readret = read (readpipe, &trace_errno, sizeof (trace_errno));
> +    }
> +  while (readret < 0 && (errno == EAGAIN || errno == EINTR));
> +
> +  if (readret < 0)
> +    error (_("Could not read trace errno: %s"), safe_strerror (errno));
> +
> +  return trace_errno;
> +}
> +
> +/* See fork-inferior.h.  */
> +
> +void
> +check_child_trace_me_errno (int readpipe)
> +{
> +  fd_set rset;
> +  struct timeval timeout;
> +  int ret;
> +
> +  /* Make sure we have a valid 'trace_me_fail_reason' function
> +     defined.  */
> +  gdb_assert (trace_me_fail_reason != nullptr);
> +
> +  FD_ZERO (&rset);
> +  FD_SET (readpipe, &rset);
> +
> +  /* Five seconds should be plenty of time to wait for the child's
> +     reply.  */
> +  timeout.tv_sec = 5;
> +  timeout.tv_usec = 0;
> +
> +  do
> +    {
> +      ret = select (readpipe + 1, &rset, NULL, NULL, &timeout);
> +    }
> +  while (ret < 0 && (errno == EAGAIN || errno == EINTR));
> +
> +  if (ret < 0)
> +    perror_with_name ("select");
> +  else if (ret == 0)
> +    error (_("Timeout while waiting for child's trace errno"));
> +  else
> +    {
> +      int child_errno;
> +
> +      child_errno = read_trace_errno_from_pipe (readpipe);
> +
> +      if (child_errno != 0)
> +	{
> +	  /* The child can't use TRACE_TRACEME.  We have to check whether
> +	     we know the reason for the failure, and then error out.  */
> +	  std::string reason = trace_me_fail_reason (child_errno);
> +
> +	  if (reason.empty ())
> +	    reason = "Could not determine reason for trace failure.";

Missing i18n.

> +
> +	  /* The child is supposed to display a warning containing the
> +	     safe_strerror message before us, so we just display the
> +	     possible reason for the failure.  */
> +	  error ("%s", reason.c_str ());
> +	}
> +    }
> +}
> +
> +/* Helper struct for fork_inferior_1, containing information on
> +   whether we should check if TRACEME_FUN was successfully called or
> +   not.  */

"was successfully called" seems ambiguous -- I first read it as,
"whether we managed to call TRACEME_FUN", which is not what you meant,
since we always call it.

It'd suggest:

 "should check whether TRACEME_FUN succeeded tracing the child"

> +
> +struct traceme_info
> +{
> +  /* True if we should check whether the call to 'traceme_fun
> +     (TRACE_ME...)' succeeded or not. */
> +  bool check_trace_me_fail_reason;
> +
> +  union
> +  {
> +    /* The non-check version of TRACEME_FUN.  It will be set if
> +       CHECK_TRACEME_FAIL_REASON is false.
> +
> +       This function will usually just perform the call to whatever
> +       trace function needed to start tracing the inferior (e.g.,
> +       ptrace).  */
> +    void (*traceme_fun_nocheck) ();
> +
> +    /* The check version of TRACEME_FUN.  It will be set if
> +       CHECK_TRACEME_FAIL_REASON is true.
> +
> +       This function will usually perform the call to whatever trace
> +       function needed to start tracing the inferior, but will also
> +       write its errno value to TRACE_ERRNO_PIPE, so that
> +       fork_inferior_1 can check whether it suceeded.  */

Typo: "suceeded" -> "succeeded".

I'd drop the "usually" both places.  It's not usually, it's always.

> +    void (*traceme_fun_check) (int trace_errno_pipe);
> +  } u;
> +};
> +
> +/* Helper function.
> +
> +   Depending on the value of TRACEME_INFO.CHECK_TRACEME_FAIL_REASON,
> +   this function will check whether the call to TRACEME_FUN succeeded
> +   or not.  */
> +
> +static pid_t
> +fork_inferior_1 (const char *exec_file_arg, const std::string &allargs,
> +		 char **env, const struct traceme_info traceme_info,
> +		 gdb::function_view<void (int)> init_trace_fun,
> +		 void (*pre_trace_fun) (),
> +		 const char *shell_file_arg,
> +		 void (*exec_fun)(const char *file, char * const *argv,
> +				  char * const *env))
>  {
>    pid_t pid;
>    /* Set debug_fork then attach to the child while it sleeps, to debug.  */
> @@ -283,6 +425,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>    int save_errno;
>    const char *inferior_cwd;
>    std::string expanded_inferior_cwd;
> +  scoped_pipe trace_pipe;

You could wrap this in a gdb::optional to avoid creating the pipe
unless necessary.  Like:

  gdb::optional<scoped_pipe> trace_pipe;
  if (traceme_info.check_trace_me_fail_reason)
    trace_pipe.emplace ();

>  
>    /* If no exec file handed to us, get it from the exec-file command
>       -- with a good, common error message if none is specified.  */
> @@ -365,12 +508,6 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>  
>    if (pid == 0)
>      {
> -      /* Close all file descriptors except those that gdb inherited
> -	 (usually 0/1/2), so they don't leak to the inferior.  Note
> -	 that this closes the file descriptors of all secondary
> -	 UIs.  */
> -      close_most_fds ();
> -
>        /* Change to the requested working directory if the user
>  	 requested it.  */
>        if (inferior_cwd != NULL)
> @@ -392,7 +529,10 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>           for the inferior.  */
>  
>        /* "Trace me, Dr. Memory!"  */
> -      (*traceme_fun) ();
> +      if (traceme_info.check_trace_me_fail_reason)
> +	(*traceme_info.u.traceme_fun_check) (trace_pipe.get_write_end ());
> +      else
> +	(*traceme_info.u.traceme_fun_nocheck) ();
>  
>        /* The call above set this process (the "child") as debuggable
>          by the original gdb process (the "parent").  Since processes
> @@ -403,6 +543,12 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>          saying "not parent".  Sorry; you'll have to use print
>          statements!  */
>  
> +      /* Close all file descriptors except those that gdb inherited
> +	 (usually 0/1/2), so they don't leak to the inferior.  Note
> +	 that this closes the file descriptors of all secondary
> +	 UIs, and the trace errno pipe (if it's open).  */
> +      close_most_fds ();
> +
>        restore_original_signals_state ();
>  
>        /* There is no execlpe call, so we have to set the environment
> @@ -431,6 +577,13 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>        _exit (0177);
>      }
>  
> +  if (traceme_info.check_trace_me_fail_reason)
> +    {
> +      /* Check the trace errno, and inform the user about the reason
> +	 of the failure, if there was any.  */
> +      check_child_trace_me_errno (trace_pipe.get_read_end ());
> +    }
> +

check_child_trace_me_errno can throw an error.  So it should be done after
we've restored all global state.  See the environ restore just below ...

>    /* Restore our environment in case a vforked child clob'd it.  */
>    environ = save_our_env;

... here.  You'd miss restoring this.

>  
> @@ -448,6 +601,48 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>    return pid;
>  }
>  
> +/* See fork-inferior.h.  */
> +
> +pid_t
> +fork_inferior (const char *exec_file_arg, const std::string &allargs,
> +	       char **env, void (*traceme_fun) (),
> +	       gdb::function_view<void (int)> init_trace_fun,
> +	       void (*pre_trace_fun) (),
> +	       const char *shell_file_arg,
> +               void (*exec_fun)(const char *file, char * const *argv,

Something odd with indentation here.  Likely tabs vs spaces.

> +                                char * const *env))
> +{
> +  struct traceme_info traceme_info;
> +
> +  traceme_info.check_trace_me_fail_reason = false;
> +  traceme_info.u.traceme_fun_nocheck = traceme_fun;
> +
> +  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
> +			  init_trace_fun, pre_trace_fun, shell_file_arg,
> +			  exec_fun);
> +}
> +
> +/* See fork-inferior.h.  */
> +
> +pid_t
> +fork_inferior (const char *exec_file_arg, const std::string &allargs,
> +	       char **env, void (*traceme_fun) (int trace_errno_pipe),
> +	       gdb::function_view<void (int)> init_trace_fun,
> +	       void (*pre_trace_fun) (),
> +	       const char *shell_file_arg,
> +               void (*exec_fun)(const char *file, char * const *argv,

Ditto re. indentation.


> +                                char * const *env))
> +{
> +  struct traceme_info traceme_info;
> +
> +  traceme_info.check_trace_me_fail_reason = true;
> +  traceme_info.u.traceme_fun_check = traceme_fun;
> +
> +  return fork_inferior_1 (exec_file_arg, allargs, env, traceme_info,
> +			  init_trace_fun, pre_trace_fun, shell_file_arg,
> +			  exec_fun);
> +}
> +
>  /* See nat/fork-inferior.h.  */
>  
>  ptid_t
> @@ -592,7 +787,7 @@ trace_start_error (const char *fmt, ...)
>  /* See nat/fork-inferior.h.  */
>  
>  void
> -trace_start_error_with_name (const char *string)
> +trace_start_error_with_name (const char *string, const char *append)
>  {
> -  trace_start_error ("%s: %s", string, safe_strerror (errno));
> +  trace_start_error ("%s: %s%s", string, safe_strerror (errno), append);
>  }
> diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
> index cf6f137edd..b67215353f 100644
> --- a/gdb/nat/fork-inferior.h
> +++ b/gdb/nat/fork-inferior.h
> @@ -32,17 +32,41 @@ struct process_stratum_target;
>  #define START_INFERIOR_TRAPS_EXPECTED 1
>  
>  /* Start an inferior Unix child process and sets inferior_ptid to its
> -   pid.  EXEC_FILE is the file to run.  ALLARGS is a string containing
> -   the arguments to the program.  ENV is the environment vector to
> -   pass.  SHELL_FILE is the shell file, or NULL if we should pick
> -   one.  EXEC_FUN is the exec(2) function to use, or NULL for the default
> -   one.  */
> -
> -/* This function is NOT reentrant.  Some of the variables have been
> -   made static to ensure that they survive the vfork call.  */
> +   pid.
> +
> +   EXEC_FILE is the file to run.
> +
> +   ALLARGS is a string containing the arguments to the program.
> +
> +   ENV is the environment vector to pass.
> +
> +   SHELL_FILE is the shell file, or NULL if we should pick one.
> +
> +   EXEC_FUN is the exec(2) function to use, or NULL for the default
> +   one.
> +
> +   This function is NOT reentrant.  Some of the variables have been
> +   made static to ensure that they survive the vfork call.
> +
> +   This function does not check whether the call to TRACEME_FUN
> +   succeeded or not.  */
>  extern pid_t fork_inferior (const char *exec_file_arg,
>  			    const std::string &allargs,
> -			    char **env, void (*traceme_fun) (),
> +			    char **env,
> +			    void (*traceme_fun) (),
> +			    gdb::function_view<void (int)> init_trace_fun,
> +			    void (*pre_trace_fun) (),
> +			    const char *shell_file_arg,
> +			    void (*exec_fun) (const char *file,
> +					      char * const *argv,
> +					      char * const *env));
> +
> +/* Like fork_inferior above, but check whether the call to TRACEME_FUN
> +   succeeded or not.  */
> +extern pid_t fork_inferior (const char *exec_file_arg,
> +			    const std::string &allargs,
> +			    char **env,
> +			    void (*traceme_fun) (int trace_errno_pipe),
>  			    gdb::function_view<void (int)> init_trace_fun,
>  			    void (*pre_trace_fun) (),
>  			    const char *shell_file_arg,
> @@ -82,9 +106,48 @@ extern void trace_start_error (const char *fmt, ...)
>    ATTRIBUTE_NORETURN ATTRIBUTE_PRINTF (1, 2);
>  
>  /* Like "trace_start_error", but the error message is constructed by
> -   combining STRING with the system error message for errno.  This
> -   function does not return.  */
> -extern void trace_start_error_with_name (const char *string)
> +   combining STRING with the system error message for errno, and
> +   (optionally) with APPEND.  This function does not return.  */
> +extern void trace_start_error_with_name (const char *string,
> +					 const char *append = "")
>    ATTRIBUTE_NORETURN;
>  
> +/* Pointer to function which can be called by
> +   'check_child_trace_me_errno' when we need to determine the reason
> +   of a e.g. 'ptrace (PTRACE_ME, ...)' failure.  ERR is the ERRNO
> +   value set by the failing ptrace call.
> +
> +   By default, the function returns an empty string (see
> +   fork-inferior.c).
> +
> +   This pointer can be overriden by targets that want to personalize
> +   the error message printed when trace fails (see linux-nat.c or
> +   gdbserver/linux-low.c, for example).  */
> +extern std::string (*trace_me_fail_reason) (int err);
> +
> +/* Check the "trace me" errno (generated when executing e.g. 'ptrace
> +   (PTRACE_ME, ...)') of the child process that was created by
> +   GDB/GDBserver when creating an inferior.  The errno value will be
> +   passed via a pipe (see 'fork_inferior'), and READPIPE is the read
> +   end of the pipe.
> +
> +   If possible (i.e., if 'trace_me_fail_reason' is defined by the
> +   target), then we also try to determine the possible reason for a
> +   failure.
> +
> +   The idea is to wait a few seconds (via 'select') until something is
> +   written on READPIPE.  When that happens, we check if the child's
> +   trace errno is different than 0.  If it is, we call the function
> +   'trace_me_fail_reason' and try to obtain the reason for the
> +   failure, and then throw an exception (with the reason as the
> +   exception's message).
> +
> +   If nothing is written on the pipe, or if 'select' fails, we also
> +   throw exceptions.  */
> +extern void check_child_trace_me_errno (int readpipe);
> +
> +/* Helper function to write TRACE_ERRNO to WRITEPIPE, which handles
> +   EINTR/EAGAIN and throws an exception if there was an error.  */
> +extern void write_trace_errno_to_pipe (int writepipe, int trace_errno);
> +
>  #endif /* NAT_FORK_INFERIOR_H */
> 

Thanks,
Pedro Alves


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error
  2020-03-17 15:47       ` [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
@ 2020-03-27 15:28         ` Pedro Alves
  2020-03-27 17:02         ` Kevin Buettner
  1 sibling, 0 replies; 98+ messages in thread
From: Pedro Alves @ 2020-03-27 15:28 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey

On 3/17/20 3:47 PM, Sergio Durigan Junior via Gdb-patches wrote:
> This patch implements the ptrace-errno-checking on the GNU/Linux
> target (both native and remote).  It builds on top of the previous
> 'fork_inferior' extension patch.
> 
> The idea is to provide a new 'traceme_fun' for each ptrace backend,
> which will accept a new integer argument representing the write end of
> the ptrace status pipe (that was created in 'fork_inferior').  This
> function will invoke the actual tracing syscall (which is 'ptrace' in
> this case), get its errno value and write it back via the pipe.  You
> can see examples of this new approach by looking at
> 'inf_ptrace_me' (GDB) or 'linux_ptrace_fun' (gdbserver).
> 
> The rest of the patch implements the necessary machinery to do
> something useful with the errno information that we received from
> 'ptrace'.
> 
> In Fedora GDB, we carry the following patch:
> 
>   https://src.fedoraproject.org/rpms/gdb/blob/8ac06474ff1e2aa4920d14e0666b083eeaca8952/f/gdb-attach-fail-reasons-5of5.patch
> 
> Its purpose is to try to detect a specific scenario where SELinux's
> 'deny_ptrace' option is enabled, which prevents GDB from ptrace'ing in
> order to debug the inferior (PTRACE_ATTACH and PTRACE_TRACEME will
> fail with EACCES in this case).
> 
> I like the idea of improving error detection and providing more
> information to the user (a simple "Permission denied" can be really
> frustrating), but I don't fully agree with the way the patch was
> implemented: it makes GDB link against libselinux only for the sake of
> consulting the 'deny_ptrace' setting, and then prints a warning if
> ptrace failed and this setting is on.
> 
> There is now a new function, 'linux_ptrace_restricted_fail_reason',
> which does a few things to check what's wrong with ptrace:
> 
>   - It dlopen's "libselinux.so.1" and checks if the "deny_ptrace"
>     option is enabled.
> 
>   - It reads the contents of "/proc/sys/kernel/yama/ptrace_scope" and
>     checks if it's different than 0.
> 
> For each of these checks, if it succeeds, the user will see a message
> informing about the restriction in place, and how it can be disabled.
> For example, if "deny_ptrace" is enabled, the user will see:
> 
>   # gdb /usr/bin/true
>   ...
>   (gdb) run
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   warning: ptrace: Permission denied
> 
>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>   from using 'ptrace'.  You can disable it by executing (as root):
> 
>     setsebool deny_ptrace off
> 
>   If you are debugging the inferior remotely, the ptrace restriction(s) must
>   be disabled in the target system (e.g., where GDBserver is running).
> 
> In case "/proc/sys/kernel/yama/ptrace_scope" is > 0:
> 
>   # gdb /usr/bin/true
>   ...
>   (gdb) run
>   Starting program: /usr/bin/true
>   warning: Could not trace the inferior process.
>   warning: ptrace: Operation not permitted
> 
>   The Linux kernel's Yama ptrace scope is in effect, which can prevent
>   GDB from using 'ptrace'.  You can disable it by executing (as root):
> 
>     echo 0 > /proc/sys/kernel/yama/ptrace_scope
> 
>   If you are debugging the inferior remotely, the ptrace restriction(s) must
>   be disabled in the target system (e.g., where GDBserver is running).
> 
> If both restrictions are enabled, both messages will show up.
> 
> This works for gdbserver as well, and actually fixes a latent bug I
> found: when ptrace is restricted, gdbserver would hang due to an
> unchecked ptrace call:
> 
>   # gdbserver :9988 /usr/bin/true
>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Operation not permitted
>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2668100 No such process
>   [ Here you would have to issue a C-c ]
> 
> Now, you will see:
> 
>   # gdbserver :9988 /usr/bin/true
>   gdbserver: linux_ptrace_test_ret_to_nx: Cannot PTRACE_TRACEME: Permission denied
>   gdbserver: linux_ptrace_test_ret_to_nx: status 256 is not WIFSTOPPED!
>   gdbserver: linux_ptrace_test_ret_to_nx: failed to kill child pid 2766868 No such process
>   gdbserver: Could not trace the inferior process.
>   gdbserver: ptrace: Permission denied
> 
>   The SELinux 'deny_ptrace' option is enabled and preventing GDB
>   from using 'ptrace'.  You can disable it by executing (as root):
> 
>     setsebool deny_ptrace off
> 
>   If you are debugging the inferior remotely, the ptrace restriction(s) need
>   to be disabled in the target system (e.g., where GDBserver is running).
>   Exiting.
>   #
> 
> (I decided to keep all the other messages, even though I find them a
> bit distracting).
> 
> If GDB can't determine the cause for the failure, it will still print
> the generic error message which tells the user to check our
> documentation:
> 
>   There might be restrictions preventing ptrace from working.  Please see
>   the appendix "Linux kernel ptrace restrictions" in the GDB documentation
>   for more details.
>   If you are debugging the inferior remotely, the ptrace restriction(s) need
>   to be disabled in the target system (e.g., where GDBserver is running).
> 
> This means that the series expands our documentation (in the next
> patch) and creates a new appendix section named "Linux kernel ptrace
> restrictions", with sub-sections for each possible restriction that
> might be in place.
> 
> Notice how, on every message, we instruct the user to "do the right
> thing" if gdbserver is being used.  This is because if the user
> started gdbserver *before* any ptrace restriction was in place, and
> then, for some reason, one or more restrictions get enabled, then the
> error message will be displayed both on gdbserver *and* on the
> connected GDB.  Since the user will be piloting GDB, it's important to
> explicitly say that the ptrace restrictions are enabled in the target,
> where gdbserver is running.
> 
> The current list of possible restrictions is:
> 
>   - SELinux's 'deny_ptrace' option (detected).
> 
>   - YAMA's /proc/sys/kernel/yama/ptrace_scope setting (detected).
> 
>   - seccomp on Docker containers (I couldn't find how to detect).
> 
> It's important to mention that all of this is Linux-specific; as far
> as I know, SELinux, YAMA and seccomp are Linux-only features.
> 
> gdb/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* inf-ptrace.c: Include "nat/fork-inferior.h".
> 	(inf_ptrace_me): New parameter "trace_errno_wpipe".  Check
> 	"ptrace" errno.
> 	(inf_ptrace_target::attach): Rewrite to use
> 	"inf_ptrace_target::ptrace_attach".
> 	(inf_ptrace_target::ptrace_attach): New function, almost
> 	identical to the previous "inf_ptrace_target::attach".
> 	* inf-ptrace.h (struct inf_ptrace_target) <int ptrace_attach>:
> 	New method.
> 	* linux-nat.c: Include "nat/fork-inferior.h".
> 	(attach_proc_task_lwp_callback): Call
> 	"linux_ptrace_attach_fail_reason_lwp" instead of
> 	"linux_ptrace_attach_fail_reason_string".
> 	(linux_nat_target::attach): Save "ERRNO".  Pass it to
> 	"linux_ptrace_attach_fail_reason".
> 	(_initialize_linux_nat): Set "trace_me_fail_reason".
> 	* nat/linux-ptrace.c: Include "gdbsupport/gdb-dlfcn.h",
> 	"nat/fork-inferior.h" and "gdbsupport/filestuff.h".
> 	(selinux_ftype): New type.
> 	(linux_ptrace_restricted_fail_reason): New function.
> 	(linux_ptrace_attach_fail_reason_1): New function, renamed
> 	from "linux_ptrace_attach_fail_reason".
> 	(linux_ptrace_attach_fail_reason): New function.
> 	(linux_ptrace_attach_fail_reason_lwp): Likewise.
> 	(linux_ptrace_me_fail_reason): Likewise.
> 	(errno_pipe): New variable.
> 	(linux_child_function): Check "ptrace" errno.  Send it through
> 	the pipe.
> 	(linux_check_ptrace_features): Initialize pipe.  Check
> 	"ptrace" errno sent through the pipe.
> 	* nat/linux-ptrace.h (linux_ptrace_attach_fail_reason): New
> 	function.
> 	(linux_ptrace_attach_fail_reason_lwp): Likewise.
> 	(linux_ptrace_me_fail_reason): Likewise.
> 	* remote.c (extended_remote_target::attach): Check error
> 	message on PACKET_ERROR.
> 	(remote_target::extended_remote_run): Likewise.
> 
> gdbserver/ChangeLog:
> yyyy-mm-dd  Sergio Durigan Junior  <sergiodj@redhat.com>
> 
> 	* linux-low.cc (linux_ptrace_fun): New parameter
> 	"trace_errno_wpipe".  Check "ptrace" errno.
> 	(attach_proc_task_lwp_callback): Call
> 	"linux_ptrace_attach_fail_reason_lwp" instead of
> 	"linux_ptrace_attach_fail_reason_string".
> 	(linux_process_target::attach): Likewise.
> 	(initialize_low): Set "trace_me_fail_reason".
> 	* server.cc (handle_v_attach): Check if "attach_inferior"
> 	succeeded.
> 	(handle_v_run): Likewise.
> 	* thread-db.cc (attach_thread): Call
> 	"linux_ptrace_attach_fail_reason_lwp" instead of
> 	"linux_ptrace_attach_fail_reason_string".
> ---
>  gdb/inf-ptrace.c        |  34 +++++++-
>  gdb/inf-ptrace.h        |   2 +
>  gdb/linux-nat.c         |  24 +++---
>  gdb/nat/fork-inferior.c |   6 +-
>  gdb/nat/fork-inferior.h |   2 +-
>  gdb/nat/linux-ptrace.c  | 178 ++++++++++++++++++++++++++++++++++++++--
>  gdb/nat/linux-ptrace.h  |  27 ++++--
>  gdb/remote.c            |  40 ++++++++-
>  gdbserver/linux-low.cc  |  31 +++++--
>  gdbserver/server.cc     |  38 ++++++++-
>  gdbserver/thread-db.cc  |   2 +-
>  11 files changed, 339 insertions(+), 45 deletions(-)
> 
> diff --git a/gdb/inf-ptrace.c b/gdb/inf-ptrace.c
> index db17a76d94..941f019709 100644
> --- a/gdb/inf-ptrace.c
> +++ b/gdb/inf-ptrace.c
> @@ -34,6 +34,7 @@
>  #include "nat/fork-inferior.h"
>  #include "utils.h"
>  #include "gdbarch.h"
> +#include "nat/fork-inferior.h"
>  
>  \f
>  
> @@ -97,10 +98,23 @@ inf_ptrace_target::remove_fork_catchpoint (int pid)
>  /* Prepare to be traced.  */
>  
>  static void
> -inf_ptrace_me (void)
> +inf_ptrace_me (int trace_errno_wpipe)
>  {
>    /* "Trace me, Dr. Memory!"  */
> -  if (ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0) < 0)
> +  int ret = ptrace (PT_TRACE_ME, 0, (PTRACE_TYPE_ARG3) 0, 0);
> +  int ptrace_errno = ret < 0 ? errno : 0;
> +
> +  try
> +    {
> +      write_trace_errno_to_pipe (trace_errno_wpipe, ptrace_errno);
> +    }
> +  catch (const gdb_exception &e)
> +    {
> +      warning ("%s", e.what ());
> +      _exit (0177);
> +    }

We've been avoiding try/catch in an async-signal-safe-only environment
(between fork and exec/exit).  This is another spot leaking in.

AFAICT, all cases of write_trace_errno_to_pipe throwing end up
caught be the immediate caller catching the error, warning and
calling _exit.  So how about doing the warning+_exit directly
within write_trace_errno_to_pipe and thus avoid the try/catch?

> +
> +  if (ret < 0)
>      trace_start_error_with_name ("ptrace");

Seems like errno was already lost when you get here, since
write_trace_errno_to_pipe clobbers errno.

BTW, you could do with a single ret < 0 check, I think?  Like:

  if (ret < 0)
   {
     int ptrace_errno = errno;

     write_trace_errno_to_pipe (trace_errno_wpipe, ptrace_errno);

     errno = ptrace_errno;

     trace_start_error_with_name ("ptrace");
   }

Though you could make (and document) write_trace_errno_to_pipe
preserve errno itself.  I'd even consider removing its
trace_errno parameter, resulting in this on the caller side:

  if (ret < 0)
   {
     write_trace_errno_to_pipe (trace_errno_wpipe);
     trace_start_error_with_name ("ptrace");
   }

Note how trace_start_error_with_name already uses the global
errno, so it wouldn't be strange.


>  }
>  
> @@ -185,6 +199,18 @@ inf_ptrace_target::mourn_inferior ()
>  
>  void
>  inf_ptrace_target::attach (const char *args, int from_tty)
> +{
> +   errno = ptrace_attach (args, from_tty);
> +   if (errno != 0)
> +     perror_with_name (("ptrace"));
> +}
> +
> +/* Attach to the process specified by ARGS.  If FROM_TTY is non-zero,
> +   be chatty about it.  Return ERRNO if the call to ptrace failed; 0
> +   otherwise.  */

This should also mention that we throw an error for other reasons.

Something like:

  Returns ERRNO if the call to ptrace failed; 0 if ptrace succeeded.
  Throws an error if it fails for reasons other than a ptrace failure.

> +
> +int
> +inf_ptrace_target::ptrace_attach (const char *args, int from_tty)
>  {
>    pid_t pid;
>    struct inferior *inf;
> @@ -223,7 +249,7 @@ inf_ptrace_target::attach (const char *args, int from_tty)
>    errno = 0;
>    ptrace (PT_ATTACH, pid, (PTRACE_TYPE_ARG3)0, 0);
>    if (errno != 0)
> -    perror_with_name (("ptrace"));
> +    return errno;
>  #else
>    error (_("This system does not support attaching to a process"));
>  #endif
> @@ -241,6 +267,8 @@ inf_ptrace_target::attach (const char *args, int from_tty)
>    set_executing (this, thr->ptid, true);
>  
>    unpusher.release ();
> +
> +  return 0;
>  }
>  
>  #ifdef PT_GET_PROCESS_STATE
> diff --git a/gdb/inf-ptrace.h b/gdb/inf-ptrace.h
> index dd0733736f..ca36ca3af3 100644
> --- a/gdb/inf-ptrace.h
> +++ b/gdb/inf-ptrace.h
> @@ -31,6 +31,8 @@ struct inf_ptrace_target : public inf_child_target
>  
>    void attach (const char *, int) override;
>  
> +  int ptrace_attach (const char *, int);
> +
>    void detach (inferior *inf, int) override;
>  
>    void resume (ptid_t, int, enum gdb_signal) override;
> diff --git a/gdb/linux-nat.c b/gdb/linux-nat.c
> index 81af83c4ac..cc044ee3ac 100644
> --- a/gdb/linux-nat.c
> +++ b/gdb/linux-nat.c
> @@ -31,6 +31,7 @@
>  #include "nat/linux-ptrace.h"
>  #include "nat/linux-procfs.h"
>  #include "nat/linux-personality.h"
> +#include "nat/fork-inferior.h"
>  #include "linux-fork.h"
>  #include "gdbthread.h"
>  #include "gdbcmd.h"
> @@ -1136,7 +1137,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>  	  else
>  	    {
>  	      std::string reason
> -		= linux_ptrace_attach_fail_reason_string (ptid, err);
> +		= linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>  	      warning (_("Cannot attach to lwp %d: %s"),
>  		       lwpid, reason.c_str ());
> @@ -1185,20 +1186,15 @@ linux_nat_target::attach (const char *args, int from_tty)
>    /* Make sure we report all signals during attach.  */
>    pass_signals ({});
>  
> -  try
> -    {
> -      inf_ptrace_target::attach (args, from_tty);
> -    }
> -  catch (const gdb_exception_error &ex)
> +  int err = inf_ptrace_target::ptrace_attach (args, from_tty);
> +
> +  if (err != 0)
>      {
>        pid_t pid = parse_pid_to_attach (args);
> -      std::string reason = linux_ptrace_attach_fail_reason (pid);
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
>  
> -      if (!reason.empty ())
> -	throw_error (ex.error, "warning: %s\n%s", reason.c_str (),
> -		     ex.what ());
> -      else
> -	throw_error (ex.error, "%s", ex.what ());
> +      error (_("warning: ptrace: %s\n%s"),
> +	       safe_strerror (err), reason.c_str ());
>      }
>  
>    /* The ptrace base target adds the main thread with (pid,0,0)
> @@ -4582,6 +4578,10 @@ Enables printf debugging output."),
>    sigemptyset (&blocked_mask);
>  
>    lwp_lwpid_htab_create ();
> +
> +  /* Set the proper function to generate a message when ptrace
> +     fails.  */
> +  trace_me_fail_reason = linux_ptrace_me_fail_reason;
>  }
>  \f
>  
> diff --git a/gdb/nat/fork-inferior.c b/gdb/nat/fork-inferior.c
> index 223ff44195..eb4c4625d7 100644
> --- a/gdb/nat/fork-inferior.c
> +++ b/gdb/nat/fork-inferior.c
> @@ -394,9 +394,9 @@ struct traceme_info
>  
>         This function will usually perform the call to whatever trace
>         function needed to start tracing the inferior, but will also
> -       write its errno value to TRACE_ERRNO_PIPE, so that
> +       write its errno value to TRACE_ERRNO_WPIPE, so that
>         fork_inferior_1 can check whether it suceeded.  */
> -    void (*traceme_fun_check) (int trace_errno_pipe);
> +    void (*traceme_fun_check) (int trace_errno_wpipe);

I was surprised to see this renaming going on this patch.  Why not
name the variable like that in the previous patch, where it was
introduced, to begin with?

>    } u;
>  };
>  
> @@ -626,7 +626,7 @@ fork_inferior (const char *exec_file_arg, const std::string &allargs,
>  
>  pid_t
>  fork_inferior (const char *exec_file_arg, const std::string &allargs,
> -	       char **env, void (*traceme_fun) (int trace_errno_pipe),
> +	       char **env, void (*traceme_fun) (int trace_errno_wpipe),

Ditto.  Etc.

>  	       gdb::function_view<void (int)> init_trace_fun,
>  	       void (*pre_trace_fun) (),
>  	       const char *shell_file_arg,
> diff --git a/gdb/nat/fork-inferior.h b/gdb/nat/fork-inferior.h
> index b67215353f..3fbead2e33 100644
> --- a/gdb/nat/fork-inferior.h
> +++ b/gdb/nat/fork-inferior.h
> @@ -66,7 +66,7 @@ extern pid_t fork_inferior (const char *exec_file_arg,
>  extern pid_t fork_inferior (const char *exec_file_arg,
>  			    const std::string &allargs,
>  			    char **env,
> -			    void (*traceme_fun) (int trace_errno_pipe),
> +			    void (*traceme_fun) (int trace_errno_wpipe),
>  			    gdb::function_view<void (int)> init_trace_fun,
>  			    void (*pre_trace_fun) (),
>  			    const char *shell_file_arg,
> diff --git a/gdb/nat/linux-ptrace.c b/gdb/nat/linux-ptrace.c
> index 5335d69092..b3fcf8bc07 100644
> --- a/gdb/nat/linux-ptrace.c
> +++ b/gdb/nat/linux-ptrace.c
> @@ -21,6 +21,9 @@
>  #include "linux-procfs.h"
>  #include "linux-waitpid.h"
>  #include "gdbsupport/buffer.h"
> +#include "gdbsupport/gdb-dlfcn.h"
> +#include "nat/fork-inferior.h"
> +#include "gdbsupport/filestuff.h"
>  #ifdef HAVE_SYS_PROCFS_H
>  #include <sys/procfs.h>
>  #endif
> @@ -30,11 +33,93 @@
>     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 *);
>  
> -std::string
> -linux_ptrace_attach_fail_reason (pid_t pid)
> +/* 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;
> +
> +  try
> +    {
> +      handle = gdb_dlopen ("libselinux.so.1");
> +    }
> +  catch (const gdb_exception_error &e)
> +    {
> +    }
> +
> +  if (handle != nullptr)
> +    {
> +      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'.  You can disable it by executing (as root):\n\
> +\n\
> +  setsebool deny_ptrace off\n"));
> +    }
> +
> +  gdb_file_up yama_ptrace_scope
> +    = gdb_fopen_cloexec ("/proc/sys/kernel/yama/ptrace_scope", "r");
> +
> +  if (yama_ptrace_scope != nullptr)
> +    {
> +      char yama_scope = fgetc (yama_ptrace_scope.get ());
> +
> +      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'.  You can disable it by executing (as root):\n\
> +\n\
> +  echo 0 > /proc/sys/kernel/yama/ptrace_scope\n"));
> +    }
> +
> +  if (ret.empty ())
> +    {
> +      /* It wasn't possible to determine the exact reason for the
> +	 ptrace error.  Let's just emit a generic error message
> +	 pointing the user to our documentation, where she can find
> +	 instructions on how to try to diagnose the problem.  */
> +      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.");
> +    }
> +
> +  /* The user may be debugging remotely, so we have to warn that
> +     the instructions above should be performed in the target.  */
> +  string_appendf (ret,
> +		  _("\n\
> +If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
> +be disabled in the target system (e.g., where GDBserver is running)."));
> +
> +  return ret;
> +}
> +
> +/* 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.  Helper for linux_ptrace_attach_fail_reason and
> +   linux_ptrace_attach_fail_reason_lwp.  */
> +
> +static std::string
> +linux_ptrace_attach_fail_reason_1 (pid_t pid)
>  {
>    pid_t tracerpid = linux_proc_get_tracerpid_nowarn (pid);
>    std::string result;
> @@ -56,10 +141,24 @@ linux_ptrace_attach_fail_reason (pid_t pid)
>  /* See linux-ptrace.h.  */
>  
>  std::string
> -linux_ptrace_attach_fail_reason_string (ptid_t ptid, int err)
> +linux_ptrace_attach_fail_reason (pid_t pid, int err)
> +{
> +  std::string result = linux_ptrace_attach_fail_reason_1 (pid);
> +  std::string ptrace_restrict = linux_ptrace_restricted_fail_reason (err);
> +
> +  if (!ptrace_restrict.empty ())
> +    result += "\n" + ptrace_restrict;
> +
> +  return result;
> +}
> +
> +/* See linux-ptrace.h.  */
> +
> +std::string
> +linux_ptrace_attach_fail_reason_lwp (ptid_t ptid, int err)
>  {
>    long lwpid = ptid.lwp ();
> -  std::string reason = linux_ptrace_attach_fail_reason (lwpid);
> +  std::string reason = linux_ptrace_attach_fail_reason_1 (lwpid);
>  
>    if (!reason.empty ())
>      return string_printf ("%s (%d), %s", safe_strerror (err), err,
> @@ -68,6 +167,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.  */
> @@ -257,6 +364,12 @@ linux_ptrace_test_ret_to_nx (void)
>  #endif /* defined __i386__ || defined __x86_64__ */
>  }
>  
> +/* If the PTRACE_TRACEME call on linux_child_function errors, we need
> +   to be able to send ERRNO back to the parent so that it can check
> +   whether there are restrictions in place preventing ptrace from
> +   working.  We do that with a pipe.  */
> +static int errno_pipe[2];
> +

We're missing a comment somewhere saying that we do that on the parent side
to avoid doing non-async-signal-safe things in the child.  Here might be
a good place, but on the gdb side we could use some comment about it too.


>  /* Helper function to fork a process and make the child process call
>     the function FUNCTION, passing CHILD_STACK as parameter.
>  
> @@ -321,7 +434,30 @@ 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);
> +  /* Close read end.  */
> +  close (errno_pipe[0]);
> +
> +  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
> +		    (PTRACE_TYPE_ARG4) 0);
> +  int ptrace_errno = ret < 0 ? errno : 0;
> +
> +  /* Write ERRNO to the pipe, even if it's zero, and close the writing
> +     end of the pipe.  */
> +  try
> +    {
> +      write_trace_errno_to_pipe (errno_pipe[1], ptrace_errno);
> +    }
> +  catch (const gdb_exception &e)
> +    {
> +      warning ("%s", e.what ());
> +      _exit (0177);
> +    }

Here's another spot that should do away with try/catch.

> +
> +  close (errno_pipe[1]);
> +
> +  if (ret != 0)
> +    trace_start_error_with_name ("ptrace");
> +
>    kill (getpid (), SIGSTOP);
>  
>    /* Fork a grandchild.  */
> @@ -346,12 +482,40 @@ linux_check_ptrace_features (void)
>    /* Initialize the options.  */
>    supported_ptrace_options = 0;
>  
> +  /* Initialize our pipe.  */
> +  if (gdb_pipe_cloexec (errno_pipe) < 0)
> +    perror_with_name ("gdb_pipe_cloexec");
> +
>    /* Fork a child so we can do some testing.  The child will call
>       linux_child_function and will get traced.  The child will
>       eventually fork a grandchild so we can test fork event
>       reporting.  */
>    child_pid = linux_fork_to_function (NULL, linux_child_function);
>  
> +  /* We don't need the write end of the pipe anymore.  */
> +  close (errno_pipe[1]);
> +
> +  try
> +    {
> +      /* Check whether 'ptrace (PTRACE_ME, ...)' failed when being
> +	 invoked by the child.  If it did, we might get the
> +	 possible reason for it as the exception message.  */
> +      check_child_trace_me_errno (errno_pipe[0]);

This is assuming that close doesn't clober errno, which in general
is not garanteed:

  https://pubs.opengroup.org/onlinepubs/9699919799/functions/errno.html#tag_16_110

"The setting of errno after a successful call to a function is unspecified 
unless the description of that function specifies that errno shall not
be modified."

A quick web search finds this for example:

 https://git.furworks.de/opensourcemirror/git/commit/06121a0a8328c8aaa7a023cf6ebb142e9dc2b45c

> +    }
> +  catch (const gdb_exception &e)
> +    {
> +      /* Close the pipe so we don't leak fd's.  */

fd's -> fds

> +      close (errno_pipe[0]);
> +
> +      /* A failure here means that PTRACE_ME failed, which means that
> +	 GDB/gdbserver will most probably not work correctly.  If we
> +	 want to be pedantic, we could just call 'exit' here.
> +	 However, let's just re-throw the exception.  */
> +      throw;
> +    }
> +
> +  close (errno_pipe[0]);
> +
>    ret = my_waitpid (child_pid, &status, 0);
>    if (ret == -1)
>      perror_with_name (("waitpid"));
> diff --git a/gdb/nat/linux-ptrace.h b/gdb/nat/linux-ptrace.h
> index 65568301f2..7cb77114ca 100644
> --- a/gdb/nat/linux-ptrace.h
> +++ b/gdb/nat/linux-ptrace.h
> @@ -176,12 +176,27 @@ struct buffer;
>  # define TRAP_HWBKPT 4
>  #endif
>  
> -extern std::string linux_ptrace_attach_fail_reason (pid_t pid);
> -
> -/* 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);
> +/* 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);
> +
> +/* Find all possible reasons we could have failed to attach to PID's
> +   LWPID and return them as a string.  ERR is the error PTRACE_ATTACH
> +   failed with (an errno).  Unlike linux_ptrace_attach_fail_reason,
> +   this function should be used when attaching to an LWP other than
> +   the leader; it does not warn about ptrace restrictions.  */
> +extern std::string linux_ptrace_attach_fail_reason_lwp (ptid_t pid, 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);
> diff --git a/gdb/remote.c b/gdb/remote.c
> index 0f78b1be1b..aacbdf1984 100644
> --- a/gdb/remote.c
> +++ b/gdb/remote.c
> @@ -5882,9 +5882,26 @@ extended_remote_target::attach (const char *args, int from_tty)
>        break;
>      case PACKET_UNKNOWN:
>        error (_("This target does not support attaching to a process"));
> +    case PACKET_ERROR:
> +      {
> +	std::string errmsg = rs->buf.data ();

This is taking a deep copy which seems unnecessary.

The code would look almost the same without it.  Like:

        const char *errmsg = rs->buf.data ();

	/* Check if we have a specific error (i.e., not a generic
	   "E01") coming from the target.  If there is, we print it
	   here.  */
	if (startswith (errmsg, "E."))
	  {
	    /* Get rid of the "E." prefix.  */
	    errmsg += 2;
	  }

	error (_("Attaching to %s failed%s%s"),
	       target_pid_to_str (ptid_t (pid)).c_str (),
	       errmsg != '\0' ? "\n" : "",
	       errmsg);


> +
> +	/* Check if we have a specific error (i.e., not a generic
> +	   "E01") coming from the target.  If there is, we print it
> +	   here.  */
> +	if (startswith (errmsg.c_str (), "E."))
> +	  {
> +	    /* Get rid of the "E." prefix.  */
> +	    errmsg.erase (0, 2);
> +	  }
> +
> +	error (_("Attaching to %s failed%s%s"),
> +	       target_pid_to_str (ptid_t (pid)).c_str (),
> +	       !errmsg.empty () ? "\n" : "",
> +	       errmsg.c_str ());
> +      }
>      default:
> -      error (_("Attaching to %s failed"),
> -	     target_pid_to_str (ptid_t (pid)).c_str ());
> +      gdb_assert_not_reached (_("bad switch"));
>      }
>  
>    set_current_inferior (remote_add_inferior (false, pid, 1, 0));
> @@ -10024,8 +10041,23 @@ remote_target::extended_remote_run (const std::string &args)
>  	error (_("Running the default executable on the remote target failed; "
>  		 "try \"set remote exec-file\"?"));
>        else
> -	error (_("Running \"%s\" on the remote target failed"),
> -	       remote_exec_file);
> +	{
> +	  std::string errmsg = rs->buf.data ();
> +
> +	  /* Check if we have a specific error (i.e., not a generic
> +	     "E01") coming from the target.  If there is, we print it
> +	     here.  */
> +	  if (startswith (errmsg.c_str (), "E."))
> +	    {
> +	      /* Get rid of the "E." prefix.  */
> +	      errmsg.erase (0, 2);
> +	    }
> +
> +	  error (_("Running \"%s\" on the remote target failed%s%s"),
> +		 remote_exec_file,
> +		 !errmsg.empty () ? "\n" : "",
> +		 errmsg.c_str ());

Ditto.

> +	}
>      default:
>        gdb_assert_not_reached (_("bad switch"));
>      }
> diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
> index 2872bc78da..42283802dd 100644
> --- a/gdbserver/linux-low.cc
> +++ b/gdbserver/linux-low.cc
> @@ -968,10 +968,24 @@ add_lwp (ptid_t ptid)
>     actually initiating the tracing of the inferior.  */
>  
>  static void
> -linux_ptrace_fun ()
> +linux_ptrace_fun (int ptrace_errno_wpipe)
>  {
> -  if (ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
> -	      (PTRACE_TYPE_ARG4) 0) < 0)
> +  int ret = ptrace (PTRACE_TRACEME, 0, (PTRACE_TYPE_ARG3) 0,
> +		    (PTRACE_TYPE_ARG4) 0);
> +  int ptrace_errno = ret < 0 ? errno : 0;
> +
> +  try
> +    {
> +      write_trace_errno_to_pipe (ptrace_errno_wpipe, ptrace_errno);
> +    }
> +  catch (const gdb_exception &e)
> +    {
> +      warning ("%s", e.what ());
> +      _exit (0177);
> +    }

Another spot that could do without try/catch.

> +
> +  errno = ptrace_errno;
> +  if (ret < 0)
>      trace_start_error_with_name ("ptrace");
>  
>    if (setpgid (0, 0) < 0)
> @@ -1170,7 +1184,7 @@ attach_proc_task_lwp_callback (ptid_t ptid)
>        else if (err != 0)
>  	{
>  	  std::string reason
> -	    = linux_ptrace_attach_fail_reason_string (ptid, err);
> +	    = linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>  	  warning (_("Cannot attach to lwp %d: %s"), lwpid, reason.c_str ());
>  	}
> @@ -1202,8 +1216,8 @@ linux_process_target::attach (unsigned long pid)
>      {
>        remove_process (proc);
>  
> -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
> -      error ("Cannot attach to process %ld: %s", pid, reason.c_str ());
> +      std::string reason = linux_ptrace_attach_fail_reason (pid, err);
> +      error (_("Cannot attach to process %ld: %s"), pid, reason.c_str ());
>      }
>  
>    /* Don't ignore the initial SIGSTOP if we just attached to this
> @@ -7552,5 +7566,10 @@ initialize_low (void)
>  
>    initialize_low_arch ();
>  
> +  /* Initialize the 'trace_me_fail_reason' function pointer.  We will
> +     use this to determine the reason for possible failures when
> +     invoking 'ptrace (PTRACE_ME, ...)'.  */
> +  trace_me_fail_reason = linux_ptrace_me_fail_reason;
> +
>    linux_check_ptrace_features ();
>  }
> diff --git a/gdbserver/server.cc b/gdbserver/server.cc
> index 43962adc86..003385f42a 100644
> --- a/gdbserver/server.cc
> +++ b/gdbserver/server.cc
> @@ -2892,9 +2892,31 @@ handle_v_attach (char *own_buf)
>  {
>    client_state &cs = get_client_state ();
>    int pid;
> +  int ret;
>  
>    pid = strtol (own_buf + 8, NULL, 16);
> -  if (pid != 0 && attach_inferior (pid) == 0)
> +
> +  if (pid <= 0)
> +    {
> +      write_enn (own_buf);
> +      return 0;
> +    }
> +
> +  try
> +    {
> +      /* Attach to the specified PID.  This function can throw, so we
> +	 make sure to catch the exception and send it (as an error
> +	 packet) back to GDB.  */
> +      ret = attach_inferior (pid);
> +    }
> +  catch (const gdb_exception_error &e)
> +    {
> +      fprintf (stderr, "%s\n", e.what ());
> +      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
> +      return 0;
> +    }
> +
> +  if (ret == 0)
>      {
>        /* Don't report shared library events after attaching, even if
>  	 some libraries are preloaded.  GDB will always poll the
> @@ -3030,7 +3052,19 @@ handle_v_run (char *own_buf)
>    free_vector_argv (program_args);
>    program_args = new_argv;
>  
> -  target_create_inferior (program_path.get (), program_args);
> +  try
> +    {
> +      /* Create the inferior.  This function can throw, so we make
> +	 sure to catch the exception and send it (as an error packet)
> +	 back to GDB.  */
> +      target_create_inferior (program_path.get (), program_args);
> +    }
> +  catch (const gdb_exception_error &e)
> +    {
> +      fprintf (stderr, "%s\n", e.what ());
> +      snprintf (own_buf, PBUFSIZ, "E.%s", e.what ());
> +      return 0;
> +    }
>  
>    if (cs.last_status.kind == TARGET_WAITKIND_STOPPED)
>      {
> diff --git a/gdbserver/thread-db.cc b/gdbserver/thread-db.cc
> index 2bb6d28820..60ceb7b663 100644
> --- a/gdbserver/thread-db.cc
> +++ b/gdbserver/thread-db.cc
> @@ -224,7 +224,7 @@ attach_thread (const td_thrhandle_t *th_p, td_thrinfo_t *ti_p)
>    err = linux_attach_lwp (ptid);
>    if (err != 0)
>      {
> -      std::string reason = linux_ptrace_attach_fail_reason_string (ptid, err);
> +      std::string reason = linux_ptrace_attach_fail_reason_lwp (ptid, err);
>  
>        warning ("Could not attach to thread %ld (LWP %d): %s",
>  	       (unsigned long) ti_p->ti_tid, ti_p->ti_lid, reason.c_str ());
> 


-- 
Thanks,
Pedro Alves


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error
  2020-03-17 15:47       ` [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
  2020-03-27 15:28         ` Pedro Alves
@ 2020-03-27 17:02         ` Kevin Buettner
  1 sibling, 0 replies; 98+ messages in thread
From: Kevin Buettner @ 2020-03-27 17:02 UTC (permalink / raw)
  To: gdb-patches

On Tue, 17 Mar 2020 11:47:18 -0400
Sergio Durigan Junior <sergiodj@redhat.com> wrote:

> +  /* The user may be debugging remotely, so we have to warn that
> +     the instructions above should be performed in the target.  */
> +  string_appendf (ret,
> +		  _("\n\
> +If you are debugging the inferior remotely, the ptrace restriction(s) must\n\
> +be disabled in the target system (e.g., where GDBserver is running)."));

It seems to me that we ought to be able to know if the user is debugging
remotely.  If possible, I'd like to see a more targeted message when
debugging remotely.  The message can then be suppressed entirely for
native debugging.  The new message might look something like this:

    You are debugging the inferior remotely, so the ptrace restriction(s)
    must be disabled in the target system (i.e. where GDBserver is running).

If that's not feasible for some reason, "e.g." should still be changed
to "i.e.".

Kevin


^ permalink raw reply	[flat|nested] 98+ messages in thread

* Re: [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name'
  2020-03-17 15:47       ` [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
@ 2020-03-27 18:20         ` Pedro Alves
  0 siblings, 0 replies; 98+ messages in thread
From: Pedro Alves @ 2020-03-27 18:20 UTC (permalink / raw)
  To: Sergio Durigan Junior, GDB Patches; +Cc: Tom Tromey

On 3/17/20 3:47 PM, Sergio Durigan Junior via Gdb-patches wrote:

> the commit that "introduced" it is:
> 
>   commit c906108c21474dfb4ed285bcc0ac6fe02cd400cc
>   Author: Stan Shebs <shebs@codesourcery.com>
>   Date:   Fri Apr 16 01:35:26 1999 +0000
> 
>       Initial creation of sourceware repository
> 
> so yeah...

Given the previous discussions, I'd appreciate this comment was
updated/clarified.

> 
> If we go to the POSIX specification for 'perror', it doesn't really
> say anything about whether errno should be preserved or not.  It does,
> however, say that 'perror's messages should be the same as those
> returned by 'strerror', and 'strerror' is not supposed to alter errno
> if the call is successful.
> 
> Maybe when our wrapper was written it was OK to modify errno, I don't
> know.  But I'd like to propose that we stick to POSIX in this case.
> 

This really has nothing to do with POSIX, because this isn't a
normal wrapper.  Control is not returning, we're throwing.  As I
mentioned before, we shouldn't be relying on errno being preserved
across throw/catch.  For _that_ reason, I'm still OK with this patch,
(even though I disagree with the given rationale).

Thanks,
Pedro Alves


^ permalink raw reply	[flat|nested] 98+ messages in thread

end of thread, other threads:[~2020-03-27 18:20 UTC | newest]

Thread overview: 98+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-08-19  3:29 [PATCH] Improve ptrace-error detection on Linux targets Sergio Durigan Junior
2019-08-19  9:10 ` Ruslan Kabatsayev
2019-08-19 13:47   ` Sergio Durigan Junior
2019-08-19 14:57     ` Ruslan Kabatsayev
2019-08-19 16:30     ` Christian Biesinger via gdb-patches
2019-08-19 17:04       ` Sergio Durigan Junior
2019-08-19 14:33 ` Eli Zaretskii
2019-08-25 22:38   ` Sergio Durigan Junior
2019-08-19 18:26 ` Pedro Alves
2019-08-25 22:40   ` Sergio Durigan Junior
2019-08-26 18:32 ` [PATCH v2] " Sergio Durigan Junior
2019-08-26 18:35   ` Christian Biesinger via gdb-patches
2019-08-26 20:51     ` Sergio Durigan Junior
2019-08-26 18:44   ` Eli Zaretskii
2019-08-29 14:40   ` Pedro Alves
2019-08-29 19:27     ` Sergio Durigan Junior
2019-08-29 19:48       ` Sergio Durigan Junior
2019-08-30 19:03         ` Pedro Alves
2019-08-30 19:51         ` [PATCH] Remove "\nError: " suffix from nat/fork-inferior.c:trace_start_error warning message Sergio Durigan Junior
2019-08-30 19:54           ` Pedro Alves
2019-08-30 21:06             ` Sergio Durigan Junior
2019-08-30 12:45       ` [PATCH v2] Improve ptrace-error detection on Linux targets Pedro Alves
2019-09-04 19:21         ` Sergio Durigan Junior
2019-09-04 19:31         ` Sergio Durigan Junior
2019-09-04 19:58           ` Pedro Alves
2019-09-04 20:21             ` Sergio Durigan Junior
2019-09-04 20:35               ` Pedro Alves
2019-09-04 20:56                 ` Sergio Durigan Junior
2019-09-04 21:23                   ` Pedro Alves
2019-09-04 21:36                     ` Sergio Durigan Junior
2019-09-05 12:19                       ` Pedro Alves
2019-09-05 17:58                         ` Sergio Durigan Junior
2019-08-30 12:47   ` Pedro Alves
2019-08-30 14:07     ` Eli Zaretskii
2019-08-30 19:44       ` Sergio Durigan Junior
2019-09-04 19:54 ` [PATCH v3] " Sergio Durigan Junior
2019-09-05 17:04   ` Eli Zaretskii
2019-09-11  1:11 ` [PATCH v4] " Sergio Durigan Junior
2019-09-12 12:39   ` Eli Zaretskii
2019-09-12 18:29     ` Sergio Durigan Junior
2019-09-24 20:40   ` Tom Tromey
2019-09-25 14:14     ` Sergio Durigan Junior
2019-09-25 22:04       ` Tom Tromey
2019-09-26  4:22         ` Sergio Durigan Junior
2019-09-26  4:22 ` [PATCH v5] " Sergio Durigan Junior
2019-09-26 17:32   ` Tom Tromey
2019-09-26 17:48     ` Pedro Alves
2019-09-26 17:51       ` Sergio Durigan Junior
2019-09-26 18:14         ` Pedro Alves
2019-09-26 18:25           ` Sergio Durigan Junior
2019-09-26 17:50     ` Sergio Durigan Junior
2019-09-26 18:13   ` Pedro Alves
2019-09-26 18:23     ` Sergio Durigan Junior
2020-02-26 20:06   ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
2020-02-26 20:06     ` [PATCH 4/6] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
2020-02-26 20:06     ` [PATCH 1/6] Introduce scoped_pipe.h Sergio Durigan Junior
2020-02-28 15:23       ` Tom Tromey
2020-02-28 16:08         ` Sergio Durigan Junior
2020-02-28 18:57           ` Tom Tromey
2020-02-28 19:48             ` Sergio Durigan Junior
2020-02-28 19:20       ` Pedro Alves
2020-02-28 19:47         ` Sergio Durigan Junior
2020-02-28 20:07           ` Pedro Alves
2020-02-26 20:06     ` [PATCH 6/6] Fix comment for 'gdb_dlopen' Sergio Durigan Junior
2020-02-26 20:23       ` Christian Biesinger via gdb-patches
2020-02-26 20:49         ` Sergio Durigan Junior
2020-02-28 15:21       ` Tom Tromey
2020-02-28 16:05         ` Sergio Durigan Junior
2020-02-26 20:06     ` [PATCH 5/6] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
2020-02-26 21:00       ` Ruslan Kabatsayev
2020-02-26 22:08         ` Sergio Durigan Junior
2020-02-26 20:06     ` [PATCH 2/6] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
2020-02-28 15:29       ` Tom Tromey
2020-02-28 16:36         ` Sergio Durigan Junior
2020-02-28 18:58           ` Tom Tromey
2020-02-28 19:50             ` Sergio Durigan Junior
2020-02-28 20:06         ` Pedro Alves
2020-02-28 20:35           ` Sergio Durigan Junior
2020-02-28 21:11             ` Pedro Alves
2020-03-02 20:07               ` Sergio Durigan Junior
2020-02-28 19:49       ` Pedro Alves
2020-02-28 20:01         ` Sergio Durigan Junior
2020-02-26 20:06     ` [PATCH 3/6] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
     [not found]     ` <87v9nh3yme.fsf@redhat.com>
2020-03-15  4:21       ` [PATCH 0/6] Improve ptrace-error detection Sergio Durigan Junior
2020-03-15 21:16         ` Kevin Buettner
2020-03-17 15:47     ` [PATCH v2 0/5] " Sergio Durigan Junior
2020-03-17 15:47       ` [PATCH v2 1/5] Introduce scoped_pipe.h Sergio Durigan Junior
2020-03-17 15:47       ` [PATCH v2 2/5] Don't reset errno/bfd_error on 'throw_perror_with_name' Sergio Durigan Junior
2020-03-27 18:20         ` Pedro Alves
2020-03-17 15:47       ` [PATCH v2 3/5] Expand 'fork_inferior' to check whether 'traceme_fun' succeeded Sergio Durigan Junior
2020-03-27  4:14         ` Kevin Buettner
2020-03-27 13:06         ` Pedro Alves
2020-03-17 15:47       ` [PATCH v2 4/5] Extend GNU/Linux to check for ptrace error Sergio Durigan Junior
2020-03-27 15:28         ` Pedro Alves
2020-03-27 17:02         ` Kevin Buettner
2020-03-17 15:47       ` [PATCH v2 5/5] Document Linux-specific possible ptrace restrictions Sergio Durigan Junior
2020-03-20  0:53       ` [PATCH v2 0/5] Improve ptrace-error detection Kevin Buettner
2020-03-24 18:23         ` Sergio Durigan Junior

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).