public inbox for gdb-patches@sourceware.org
 help / color / mirror / Atom feed
From: Thiago Jung Bauermann <thiago.bauermann@linaro.org>
To: gdb-patches@sourceware.org
Cc: Thiago Jung Bauermann <thiago.bauermann@linaro.org>,
	Pedro Alves <pedro@palves.net>
Subject: [PATCH 3/5] gdbserver/linux: Read auxv from any thread of the process
Date: Fri, 31 Mar 2023 03:44:30 +0000	[thread overview]
Message-ID: <20230331034432.3037148-4-thiago.bauermann@linaro.org> (raw)
In-Reply-To: <20230331034432.3037148-1-thiago.bauermann@linaro.org>

If the initial thread of the process exits, reading the process' auxiliary
vector via /proc/PID/auxv fails in one of two ways:

1. If gdbserver is root, then opening the file succeeds but reading from it
   returns 0 bytes.

2. If gdbserver isn't root, then opening the file fails with EACCES.

This race isn't easy to run into because one of the first things that GDB
does when connecting to gdbserver is to read the inferior's auxiliary
vector and store it in the auxv cache.  All further queries of the auxiliary
vector will be served from there, unless one of the cache-clearing
events ("inferior_exit", "inferior_appeared", "executable_changed") occurs.

To fix the race condition iterate through tasks in /proc/PID/task/ and
try to read their auxv file, returning the first one that succeeds.

Suggested-by: Pedro Alves <pedro@palves.net>
---
 gdb/nat/linux-procfs.c | 67 ++++++++++++++++++++++++++++++++++++++++++
 gdb/nat/linux-procfs.h |  7 +++++
 gdbserver/linux-low.cc | 21 +++----------
 3 files changed, 78 insertions(+), 17 deletions(-)

diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index 9c2d1beb91fa..d53d3ea98bed 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -329,6 +329,73 @@ linux_proc_attach_tgid_threads (pid_t pid,
 
 /* See linux-procfs.h.  */
 
+bool
+linux_proc_read_auxv (pid_t pid, gdb_byte *readbuf, off_t offset, size_t len,
+		      ssize_t &xfered_len)
+{
+  if (linux_proc_get_tgid (pid) != pid)
+    return false;
+
+  std::string path_tasks = string_printf ("/proc/%ld/task", (long) pid);
+  gdb_dir_up dir (opendir (path_tasks.c_str ()));
+  if (dir == nullptr)
+    {
+      warning (_("Could not open /proc/%ld/task."), (long) pid);
+      return false;
+    }
+
+  /* In a multi-threaded process, any given thread (including the initial one)
+     can exit at any time.  Because of this, iterate through the threads trying
+     to read their auxv file and return the first one that succeeds.  */
+  struct dirent *dp;
+  while ((dp = readdir (dir.get ())) != nullptr)
+    {
+      /* Fetch one lwp.  */
+      unsigned long lwp = strtoul (dp->d_name, nullptr, 10);
+      if (lwp == 0)
+	continue;
+
+      std::string path_auxv = string_printf ("/proc/%lu/auxv", lwp);
+      scoped_fd fd = gdb_open_cloexec (path_auxv, O_RDONLY, 0);
+      if (fd.get () < 0)
+	{
+	  /* If the leader thread of the process exited and we aren't root, then
+	     trying to read its auxv file results in EACCES error.  */
+	  if (errno == EACCES)
+	    continue;
+
+	  /* Unexpected failure.  Give up.  */
+	  return false;
+	}
+
+      ssize_t l;
+      if (offset != 0 && lseek (fd.get (), offset, SEEK_SET) != offset)
+	l = -1;
+      else
+	l = read (fd.get (), readbuf, len);
+
+      /* Unexpected failure.  Give up.  */
+      if (l < 0)
+	return false;
+      /* The read call returns 0 if the leader thread of the process exited and
+	 we are root, or if any thread exited after we opened its auxv file but
+	 before we had a chance to read it.  This only applies during the first
+	 read into the file though (i.e., offset == 0).  */
+      else if (l != 0 || offset != 0)
+	{
+	  xfered_len = l;
+	  return true;
+	}
+    }
+
+  /* If we get here, either all open call failed with EACCES (meaning the
+     threads are gone), or all read calls returned 0 (meaning either EOF or that
+     the threads are gone).  */
+  return true;
+}
+
+/* See linux-procfs.h.  */
+
 int
 linux_proc_task_list_dir_exists (pid_t pid)
 {
diff --git a/gdb/nat/linux-procfs.h b/gdb/nat/linux-procfs.h
index 639d8efaf0bf..bcdcedcdc2a9 100644
--- a/gdb/nat/linux-procfs.h
+++ b/gdb/nat/linux-procfs.h
@@ -80,6 +80,13 @@ extern int linux_proc_task_list_dir_exists (pid_t pid);
 
 extern const char *linux_proc_pid_to_exec_file (int pid);
 
+/* Read the auxiliary vector for the PID process into READBUF which has LEN bytes,
+   starting at OFFSET.  Returns true if successful, with XFERED_LEN indicating how many
+   bytes were read, and false on error.  */
+
+extern bool linux_proc_read_auxv (pid_t pid, gdb_byte *readbuf, off_t offset,
+				  size_t len, ssize_t &xfered_len);
+
 /* Display possible problems on this system.  Display them only once
    per GDB execution.  */
 
diff --git a/gdbserver/linux-low.cc b/gdbserver/linux-low.cc
index e6a39202a98a..d42eb3a49ad7 100644
--- a/gdbserver/linux-low.cc
+++ b/gdbserver/linux-low.cc
@@ -5486,24 +5486,11 @@ int
 linux_process_target::read_auxv (int pid, CORE_ADDR offset,
 				 unsigned char *myaddr, unsigned int len)
 {
-  char filename[PATH_MAX];
-  int fd, n;
-
-  xsnprintf (filename, sizeof filename, "/proc/%d/auxv", pid);
-
-  fd = open (filename, O_RDONLY);
-  if (fd < 0)
-    return -1;
-
-  if (offset != (CORE_ADDR) 0
-      && lseek (fd, (off_t) offset, SEEK_SET) != (off_t) offset)
-    n = -1;
-  else
-    n = read (fd, myaddr, len);
-
-  close (fd);
+  ssize_t xfered_len;
+  bool rc = linux_proc_read_auxv ((pid_t) pid, (gdb_byte *) myaddr,
+				  (off_t) offset, (size_t) len, xfered_len);
 
-  return n;
+  return rc ? (int) xfered_len : -1;
 }
 
 int

  parent reply	other threads:[~2023-03-31  3:45 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2023-03-31  3:44 [PATCH 0/5] gdbserver: Follow-up on linux_get_auxv using PID parameter Thiago Jung Bauermann
2023-03-31  3:44 ` [PATCH 1/5] gdbserver: Use current_process in handle_qxfer_auxv Thiago Jung Bauermann
2023-03-31  3:44 ` [PATCH 2/5] gdbserver: Use the PID of the current process instead of the current thread Thiago Jung Bauermann
2023-03-31  3:44 ` Thiago Jung Bauermann [this message]
2023-03-31  3:44 ` [PATCH 4/5] gdb/linux-nat: Read auxv from any thread of the process Thiago Jung Bauermann
2023-03-31  3:44 ` [PATCH 5/5] gdb/testsuite: Add test that reads auxv in a multi-threaded inferior Thiago Jung Bauermann
2023-04-04 14:00   ` Alexandra Petlanova Hajkova
2023-04-04 15:19 ` [PATCH 0/5] gdbserver: Follow-up on linux_get_auxv using PID parameter Simon Marchi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20230331034432.3037148-4-thiago.bauermann@linaro.org \
    --to=thiago.bauermann@linaro.org \
    --cc=gdb-patches@sourceware.org \
    --cc=pedro@palves.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).