Mirror of the gdb-patches mailing list
 help / color / mirror / Atom feed
* [PATCH 1/2] [gdb] Add contrib/sudo-allow-ptrace.sh
@ 2024-03-25 13:45 Tom de Vries
  2024-03-25 13:45 ` [PATCH 2/2] [gdb] Fix gdb.base/attach-deleted-exec.exp with sudo-allow-ptrace.sh Tom de Vries
  0 siblings, 1 reply; 2+ messages in thread
From: Tom de Vries @ 2024-03-25 13:45 UTC (permalink / raw)
  To: gdb-patches

Some linux systems have the setting kernel.yama.ptrace_scope set to 1 or 2.
This limits the ability to attach to processes, for security reasons.
However, this can get in the way of for instance:
- debugging an application, and
- running certain test-cases in the gdb testsuite.

This can be worked around by setting kernel.yama.ptrace_scope to 0, either:
- temporarily (until the next reboot), using:
  - "echo 0 | sudo tee /proc/sys/kernel/yama/ptrace_scope", or
  - "sudo sysctl -w kernel.yama.ptrace_scope=0",
- or permanently, by editing /etc/sysctl.conf or adding a file to
  /etc/sysctl.d.

However, it may be the case that setting kernel.yama.ptrace_scope to 0 is not
desirable, for instance when trying to debug an application on a production
system.

Another way of working around this is by running as root, but this may be
undesirable as well.

Here ( https://wiki.archlinux.org/title/Capabilities ) it's demonstrated how
to run gdb while temporarily adding the CAP_SYS_PTRACE capability using capsh.

I tried out this approach on the test-suite, and found that while capsh uses
"--user $USER", some things are different from being $USER:
- $HOME is /root, not /home/$USER
- USER and LOGNAME are root
- ulimit -c is 0, even though I set it to unlimited in /home/$USER/.bashrc.

Add a convenience script gdb/contrib/sudo-allow-ptrace.sh that takes care of
these differences.

With the script, I'm able to run the test-suite as usual on a
kernel.yama.ptrace_scope=1/2 system.

There's only one regression compared to kernel.yama.ptrace_scope=0, in
gdb.base/attach-deleted-exec.exp, which is filed as PR gdb/31528.  A following
patch deals with this.

Tested and shell-checked on x86_64-linux.

PR external/31520
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31520
---
 gdb/contrib/sudo-allow-ptrace.sh | 110 +++++++++++++++++++++++++++++++
 1 file changed, 110 insertions(+)
 create mode 100755 gdb/contrib/sudo-allow-ptrace.sh

diff --git a/gdb/contrib/sudo-allow-ptrace.sh b/gdb/contrib/sudo-allow-ptrace.sh
new file mode 100755
index 00000000000..f46ba7cd9ab
--- /dev/null
+++ b/gdb/contrib/sudo-allow-ptrace.sh
@@ -0,0 +1,110 @@
+#!/bin/sh
+
+# Copyright (C) 2024 Free Software Foundation, Inc.
+# 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/>.
+
+# This script intends to facilitate using gdb to attach to processes
+# on a kernel.yama.ptrace_scope=1 system, without changing the setting and
+# without becoming root.
+#
+# Example usage (running the gdb testsuite):
+#   $ cd build/gdb/testsuite
+#   $ sudo-allow-ptrace.sh make check
+#
+# Example usage (using gdb to attach to process):
+#   $ sudo-allow-ptrace.sh gdb -p <pid>
+#
+# The script is based on this [1] recipe.
+#
+# [1] https://wiki.archlinux.org/title/Capabilities.
+
+set -e
+
+case " $1 " in
+    " --stage2 ")
+	stage=2
+	shift
+	;;
+
+    " --stage3 ")
+	stage=3
+	shift
+	;;
+
+    *)
+	stage=1
+	;;
+esac
+
+if [ $stage = 1 ]; then
+    # STAGE 1, as user $USER.
+
+    # shellcheck disable=SC3045
+    ulimit_core_hard=$(ulimit -Hc)
+    # shellcheck disable=SC3045
+    ulimit_core_soft=$(ulimit -Sc)
+
+    exec \
+	sudo -E \
+	"$0" \
+	--stage2 \
+	"$USER" \
+	"$HOME" \
+	"$ulimit_core_hard" \
+	"$ulimit_core_soft" \
+	"$@"
+elif [ $stage = 2 ]; then
+    # STAGE 2, as user root.
+
+    export user="$1"
+    shift
+
+    export home="$1"
+    shift
+
+    ulimit_core_hard="$1"
+    shift
+
+    ulimit_core_soft="$1"
+    shift
+
+    # shellcheck disable=SC3045
+    ulimit -Hc "$ulimit_core_hard"
+    # shellcheck disable=SC3045
+    ulimit -Sc "$ulimit_core_soft"
+
+    exec \
+	capsh \
+	--caps="cap_setpcap,cap_setuid,cap_setgid+ep cap_sys_ptrace+eip" \
+	--keep=1 \
+	--user="$user" \
+	--addamb="cap_sys_ptrace" \
+	--shell="$0" \
+	-- \
+	--stage3 \
+	"$user" \
+	"$home" \
+	"$@"
+elif [ $stage = 3 ]; then
+    # STAGE 3, as user root with "assumed identity" $USER.
+
+    export USER="$1"
+    export LOGNAME="$1"
+    shift
+
+    export HOME="$1"
+    shift
+
+    exec "$@"
+fi

base-commit: 61ced226a4fc2e6df7836cd9c0f7e1ad47af2440
-- 
2.35.3


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

* [PATCH 2/2] [gdb] Fix gdb.base/attach-deleted-exec.exp with sudo-allow-ptrace.sh
  2024-03-25 13:45 [PATCH 1/2] [gdb] Add contrib/sudo-allow-ptrace.sh Tom de Vries
@ 2024-03-25 13:45 ` Tom de Vries
  0 siblings, 0 replies; 2+ messages in thread
From: Tom de Vries @ 2024-03-25 13:45 UTC (permalink / raw)
  To: gdb-patches

When running test-case gdb.base/attach-deleted-exec.exp with script
gdb/contrib/sudo-allow-ptrace.sh, we run into:
...
(gdb) attach 2804069^M
Attaching to process 2804069^M
No executable file now.^M
warning: Could not load vsyscall page because no executable was specified^M
0x0000ffff79cd83c8 in ?? ()^M
(gdb) FAIL: gdb.base/attach-deleted-exec.exp: \
  attach to process with deleted executable
...

The script sudo-allow-ptrace.sh was introduced to work around
kernel.yama.ptrace_scope being set to 1 or 2, but this fail also happens for
kernel.yama.ptrace_scope=0.

The root cause for the fail is the failing "access (name, R_OK) == 0" check in
linux_proc_pid_to_exec_file:
...
  /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe
     can be.  */
  if (access (buf, R_OK) != 0 && access (name, R_OK) == 0)
    strcpy (buf, name);
...

In other words, the system says there's no read permission for /proc/PID/exe.

Confusingly though, reading /proc/PID/exe works fine, so there seems to be a
contradiction here.

This behaviour can be minimally reproduced using:
...
$ cat try.sh

kill -9 $(pidof mysleep) 2> /dev/null

cp /usr/bin/sleep mysleep
md5sum mysleep
./mysleep 10000 &

(
    sleep 1
    pid=$(pidof mysleep)
    echo "PID: $pid"
    test -r /proc/$pid/exe
    echo $?
    md5sum /proc/$pid/exe
    kill -9 $(pidof mysleep) 2> /dev/null
)
...
and:
...
$ cat ./try2.sh

sudo \
    -E \
    capsh \
    --caps="cap_setpcap,cap_setuid,cap_setgid+ep cap_sys_ptrace+eip" \
    --keep=1 \
    --user=$USER \
    --addamb="cap_sys_ptrace" \
    --shell=./try.sh --
...
which shows:
...
$ ./try2.sh
[sudo] password for root:
6a85b2e53dce34ce2c35129b5b20c50b  mysleep
PID: 4536
1
6a85b2e53dce34ce2c35129b5b20c50b  /proc/4536/exe
...
where:
- according to test -r, we cannot read /proc/$pid/exe
- but according to md5sum, we can read /proc/$pid/exe

This was filed as a potential kernel PR here (
https://bugzilla.suse.com/show_bug.cgi?id=1221867 ).

As for gdb, fix or workaround this by dropping the "access (name, R_OK) == 0"
check.

While reading about /proc/PID/exe I came across:
- https://bugzilla.suse.com/show_bug.cgi?id=1216352
- https://bugzilla.kernel.org/show_bug.cgi?id=211593
which advise avoiding using readlink on /proc/PID/exec before reading.

I've looked briefly into fixing this, but found that it's not trivial, so
for now I've added a FIXME comment in linux_proc_pid_to_exec_file.

PR gdb/31528
Bug: https://sourceware.org/bugzilla/show_bug.cgi?id=31528
---
 gdb/nat/linux-procfs.c | 25 ++++++++++++++++++++++---
 1 file changed, 22 insertions(+), 3 deletions(-)

diff --git a/gdb/nat/linux-procfs.c b/gdb/nat/linux-procfs.c
index b17e3120792..a3a16ed93af 100644
--- a/gdb/nat/linux-procfs.c
+++ b/gdb/nat/linux-procfs.c
@@ -345,6 +345,23 @@ linux_proc_pid_to_exec_file (int pid)
   char name[PATH_MAX];
   ssize_t len;
 
+  /* FIXME: calling readlink to determine the file to read symbols from is
+     problematic.  Consider the scenario where:
+     - we run an application /foo/bar,
+     - we use gdb to attach to the running application,
+     - gdb calls linux_proc_pid_to_exec_file to the get the filename to read
+       the symbols from,
+     - linux_proc_pid_to_exec_file calls readlink on /proc/PID/exe, and
+       returns /foo/bar,
+     - we remove /foo/bar, and
+     - gdb tries to read the symbols from /foo/bar, and fails.
+     At this point we can still read the symbols from /proc/PID/exe.
+
+     See also:
+     - https://bugzilla.suse.com/show_bug.cgi?id=1216352
+     - https://bugzilla.kernel.org/show_bug.cgi?id=211593
+  */
+
   xsnprintf (name, PATH_MAX, "/proc/%d/exe", pid);
   len = readlink (name, buf, PATH_MAX - 1);
   if (len <= 0)
@@ -352,9 +369,11 @@ linux_proc_pid_to_exec_file (int pid)
   else
     buf[len] = '\0';
 
-  /* Use /proc/PID/exe if the actual file can't be read, but /proc/PID/exe
-     can be.  */
-  if (access (buf, R_OK) != 0 && access (name, R_OK) == 0)
+  /* Use /proc/PID/exe if the actual file can't be read.  Note that we don't
+     check for "access ("/proc/PID/exe", R_OK) == 0".  It possible that this
+     check will fail while we can actually read /proc/PID/exe (
+     https://bugzilla.suse.com/show_bug.cgi?id=1221867 ).  */
+  if (access (buf, R_OK) != 0)
     strcpy (buf, name);
 
   return buf;
-- 
2.35.3


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

end of thread, other threads:[~2024-03-25 13:45 UTC | newest]

Thread overview: 2+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2024-03-25 13:45 [PATCH 1/2] [gdb] Add contrib/sudo-allow-ptrace.sh Tom de Vries
2024-03-25 13:45 ` [PATCH 2/2] [gdb] Fix gdb.base/attach-deleted-exec.exp with sudo-allow-ptrace.sh Tom de Vries

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox