guile-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH] Throw an exception when mutating read-only data


From: Andy Wingo
Subject: [PATCH] Throw an exception when mutating read-only data
Date: Sun, 2 Apr 2017 12:11:31 +0200

* libguile/init.c (scm_i_init_guile): Install the SIGSEGV handler unless
  GUILE_INSTALL_SIGSEGV_HANDLER is 0.
* libguile/loader.c
  (scm_maybe_throw_exception_for_mutation_of_read_only_data): New public
  function.
  (sigsegv_handler): New helper.
  (scm_install_sigsegv_handler): New public function.
* libguile/loader.h: Declare new API.
---
 libguile/init.c   |  7 +++++
 libguile/loader.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++
 libguile/loader.h |  3 +++
 3 files changed, 87 insertions(+)

diff --git a/libguile/init.c b/libguile/init.c
index b046685d4..b333e7def 100644
--- a/libguile/init.c
+++ b/libguile/init.c
@@ -530,6 +530,13 @@ scm_i_init_guile (void *base)
   scm_init_rw ();
   scm_init_extensions ();
 
+  if (scm_getenv_int ("GUILE_INSTALL_SIGSEGV_HANDLER", 1))
+    {
+      if (scm_install_sigsegv_handler () != 0)
+        perror ("failed to install SIGSEGV handler");
+      /* Continue regardless.  */
+    }
+
   atexit (cleanup_for_exit);
   scm_load_startup_files ();
   scm_init_load_should_auto_compile ();
diff --git a/libguile/loader.c b/libguile/loader.c
index 7b1adc9c9..d12137be3 100644
--- a/libguile/loader.c
+++ b/libguile/loader.c
@@ -745,6 +745,83 @@ scm_all_mapped_elf_images (void)
   return result;
 }
 
+void
+scm_maybe_throw_exception_for_mutation_of_read_only_data (void *addr)
+{
+  if (!find_mapped_elf_image_unlocked (addr))
+    return;
+
+  /* Assume that a SEGV originating from access to an address in our
+     mapped ELF images is because that part of the image was mapped
+     read-only, and user code is trying to mutate it.  Throw an
+     exception instead.  */
+  scm_misc_error (NULL, "Attempt to mutate read-only value", SCM_EOL);
+}
+
+static struct sigaction prev_sigsegv_handler;
+
+static void
+sigsegv_handler (int sig, siginfo_t *si, void *unused)
+{
+  scm_maybe_throw_exception_for_mutation_of_read_only_data (si->si_addr);
+
+  /* If we got here, we need to chain up to the previously installed
+     handler.  */
+  if (prev_sigsegv_handler.sa_flags & SA_SIGINFO)
+    {
+      if (prev_sigsegv_handler.sa_sigaction == SIG_IGN)
+        /* Although it's not meaningful to continue after ignoring
+           SIGSEGV, that's exactly what the user requested, so just
+           return.  */
+        return;
+
+      if (prev_sigsegv_handler.sa_sigaction != SIG_DFL)
+        {
+          prev_sigsegv_handler.sa_sigaction (sig, si, unused);
+          /* The meaning of a program is undefined after returning from
+             SIGSEGV, but that's what the previous handler did; respect
+             its judgment.  */
+          return;
+        }
+    }
+  else
+    {
+      if (prev_sigsegv_handler.sa_handler == SIG_IGN)
+        /* See note above.  */
+        return;
+
+      if (prev_sigsegv_handler.sa_handler != SIG_DFL)
+        {
+          prev_sigsegv_handler.sa_handler (sig);
+          /* See note above.  */
+          return;
+        }
+    }
+
+  /* If we got here, the previous handler was SIG_DFL which will abort
+     the program.  Restore SIG_DFL and re-raise the signal.  */
+  {
+    struct sigaction sa;
+    sigemptyset (&sa.sa_mask);
+    sa.sa_handler = SIG_DFL;
+    if (sigaction (sig, &sa, NULL) != 0)
+      abort ();
+    raise (sig);
+    /* Unreachable.  */
+    abort ();
+  }
+}
+
+int
+scm_install_sigsegv_handler (void)
+{
+  struct sigaction sa;
+  sa.sa_flags = SA_SIGINFO;
+  sigemptyset (&sa.sa_mask);
+  sa.sa_sigaction = sigsegv_handler;
+  return sigaction (SIGSEGV, &sa, &prev_sigsegv_handler);
+}
+
 struct frame_map_prefix
 {
   scm_t_uint32 text_offset;
diff --git a/libguile/loader.h b/libguile/loader.h
index 5c719cbce..f097a6ccc 100644
--- a/libguile/loader.h
+++ b/libguile/loader.h
@@ -27,6 +27,9 @@ SCM_API SCM scm_load_thunk_from_memory (SCM bv);
 SCM_INTERNAL const scm_t_uint8 *
 scm_find_slot_map_unlocked (const scm_t_uint32 *ip);
 
+SCM_API int scm_install_sigsegv_handler (void);
+SCM_API void scm_maybe_throw_exception_for_mutation_of_read_only_data (void *);
+
 SCM_INTERNAL void scm_bootstrap_loader (void);
 SCM_INTERNAL void scm_init_loader (void);
 
-- 
2.12.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]