206 lines
4.8 KiB
Diff
206 lines
4.8 KiB
Diff
From 8eab929c763e3a951794968f1a7d2f99a83c4f9d Mon Sep 17 00:00:00 2001
|
|
From: Stef Walter <stefw@redhat.com>
|
|
Date: Thu, 27 Aug 2015 09:37:10 +0200
|
|
Subject: [PATCH 1/2] common: Produce better backtraces with gdb when possible
|
|
|
|
Note that this may require this sysctl to be set on the system:
|
|
|
|
/proc/sys/kernel/yama/ptrace_scope = 0
|
|
|
|
https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace%20Protection
|
|
|
|
Reviewed-by: Peter <petervo@redhat.com>
|
|
---
|
|
.travis.yml | 2 +-
|
|
src/common/cockpittest.c | 155 ++++++++++++++++++++++++++++++++++++++++++++++-
|
|
tools/cockpit.spec | 1 +
|
|
3 files changed, 154 insertions(+), 4 deletions(-)
|
|
|
|
diff --git a/src/common/cockpittest.c b/src/common/cockpittest.c
|
|
index 5501681..bf20b11 100644
|
|
--- a/src/common/cockpittest.c
|
|
+++ b/src/common/cockpittest.c
|
|
@@ -39,6 +39,10 @@
|
|
#include <netinet/ip6.h>
|
|
#include <ifaddrs.h>
|
|
|
|
+#include <sys/select.h>
|
|
+#include <fcntl.h>
|
|
+#include <sys/wait.h>
|
|
+
|
|
/*
|
|
* HACK: We can't yet use g_test_expect_message() and friends.
|
|
* They were pretty broken until GLib 2.40 if you have any debug
|
|
@@ -466,20 +470,165 @@ _cockpit_assert_bytes_eq_msg (const char *domain,
|
|
expect, exp_len);
|
|
}
|
|
|
|
+/*
|
|
+ * This gdb code only works if /proc/sys/kernel/yama/ptrace_scope is set to zero
|
|
+ * See: https://wiki.ubuntu.com/SecurityTeam/Roadmap/KernelHardening#ptrace%20Protection
|
|
+ */
|
|
+
|
|
+static gboolean stack_trace_done = FALSE;
|
|
+
|
|
+static void
|
|
+stack_trace_sigchld (int signum)
|
|
+{
|
|
+ stack_trace_done = TRUE;
|
|
+}
|
|
+
|
|
+static void
|
|
+stack_trace (char **args)
|
|
+{
|
|
+ pid_t pid;
|
|
+ int in_fd[2];
|
|
+ int out_fd[2];
|
|
+ fd_set fdset;
|
|
+ fd_set readset;
|
|
+ struct timeval tv;
|
|
+ int sel, idx, state;
|
|
+ char buffer[256];
|
|
+ char c;
|
|
+
|
|
+ stack_trace_done = FALSE;
|
|
+ signal (SIGCHLD, stack_trace_sigchld);
|
|
+
|
|
+ if ((pipe (in_fd) == -1) || (pipe (out_fd) == -1))
|
|
+ {
|
|
+ perror ("unable to open pipe");
|
|
+ _exit (0);
|
|
+ }
|
|
+
|
|
+ pid = fork ();
|
|
+ if (pid == 0)
|
|
+ {
|
|
+ /* Save stderr for printing failure below */
|
|
+ int old_err = dup (2);
|
|
+ fcntl (old_err, F_SETFD, fcntl (old_err, F_GETFD) | FD_CLOEXEC);
|
|
+
|
|
+ close (0); dup (in_fd[0]); /* set the stdin to the in pipe */
|
|
+ close (1); dup (out_fd[1]); /* set the stdout to the out pipe */
|
|
+
|
|
+ execvp (args[0], args); /* exec gdb */
|
|
+
|
|
+ /* Print failure to original stderr */
|
|
+ perror ("exec gdb failed");
|
|
+ _exit (0);
|
|
+ }
|
|
+ else if (pid == (pid_t) -1)
|
|
+ {
|
|
+ perror ("unable to fork");
|
|
+ _exit (0);
|
|
+ }
|
|
+
|
|
+ FD_ZERO (&fdset);
|
|
+ FD_SET (out_fd[0], &fdset);
|
|
+
|
|
+ write (in_fd[1], "backtrace\n", 10);
|
|
+ write (in_fd[1], "quit\n", 5);
|
|
+
|
|
+ idx = 0;
|
|
+ state = 0;
|
|
+
|
|
+ while (1)
|
|
+ {
|
|
+ readset = fdset;
|
|
+ tv.tv_sec = 1;
|
|
+ tv.tv_usec = 0;
|
|
+
|
|
+ sel = select (FD_SETSIZE, &readset, NULL, NULL, &tv);
|
|
+ if (sel == -1)
|
|
+ break;
|
|
+
|
|
+ if ((sel > 0) && (FD_ISSET (out_fd[0], &readset)))
|
|
+ {
|
|
+ if (read (out_fd[0], &c, 1))
|
|
+ {
|
|
+ switch (state)
|
|
+ {
|
|
+ case 0:
|
|
+ if (c == '#')
|
|
+ {
|
|
+ state = 1;
|
|
+ idx = 0;
|
|
+ buffer[idx++] = c;
|
|
+ }
|
|
+ break;
|
|
+ case 1:
|
|
+ buffer[idx++] = c;
|
|
+ if ((c == '\n') || (c == '\r'))
|
|
+ {
|
|
+ buffer[idx] = 0;
|
|
+ fprintf (stderr, "%s", buffer);
|
|
+ state = 0;
|
|
+ idx = 0;
|
|
+ }
|
|
+ break;
|
|
+ default:
|
|
+ break;
|
|
+ }
|
|
+ }
|
|
+ }
|
|
+ else if (stack_trace_done)
|
|
+ break;
|
|
+ }
|
|
+
|
|
+ close (in_fd[0]);
|
|
+ close (in_fd[1]);
|
|
+ close (out_fd[0]);
|
|
+ close (out_fd[1]);
|
|
+ _exit (0);
|
|
+}
|
|
+
|
|
+static void
|
|
+gdb_stack_trace (void)
|
|
+{
|
|
+ pid_t pid;
|
|
+ gchar buf[16];
|
|
+ gchar *args[4] = { "gdb", "-p", buf, NULL };
|
|
+ int status;
|
|
+
|
|
+ sprintf (buf, "%u", (guint) getpid ());
|
|
+
|
|
+ pid = fork ();
|
|
+ if (pid == 0)
|
|
+ {
|
|
+ stack_trace (args);
|
|
+ _exit (0);
|
|
+ }
|
|
+ else if (pid == (pid_t) -1)
|
|
+ {
|
|
+ perror ("unable to fork gdb");
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ waitpid (pid, &status, 0);
|
|
+}
|
|
+
|
|
void
|
|
cockpit_test_signal_backtrace (int sig)
|
|
{
|
|
void *array[16];
|
|
size_t size;
|
|
|
|
- // get void*'s for all entries on the stack
|
|
+ signal (sig, SIG_DFL);
|
|
+
|
|
+ /* Try to trace with gdb first */
|
|
+ gdb_stack_trace ();
|
|
+
|
|
+ /* In case above didn't work, print raw stack trace */
|
|
size = backtrace (array, G_N_ELEMENTS (array));
|
|
|
|
- // print out all the frames to stderr
|
|
+ /* print out all the frames to stderr */
|
|
fprintf (stderr, "Error: signal %s:\n", strsignal (sig));
|
|
backtrace_symbols_fd (array, size, STDERR_FILENO);
|
|
|
|
- signal (sig, SIG_DFL);
|
|
raise (sig);
|
|
}
|
|
|
|
--
|
|
2.4.3
|
|
|