summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Eggert <eggert@cs.ucla.edu>2012-10-01 15:12:44 -0700
committerPaul Eggert <eggert@cs.ucla.edu>2012-10-01 15:12:44 -0700
commitaa1ba90e4a95542c83cf636de3bc67e8fb23bad3 (patch)
tree1407a999bbc11bf54aaeba40764d6a75565db182
parentace917bddb2ed2448a97ddf279445bb581c5cd32 (diff)
downloademacs-aa1ba90e4a95542c83cf636de3bc67e8fb23bad3.tar.gz
Fix a malloc race condition involving strsignal.
A signal can arrive in the middle of a malloc, and Emacs's signal handler can invoke strsignal, which can invoke malloc, which is not portable. This race condition bug makes Emacs hang on GNU/Linux. Fix it by altering the signal handler so that it does not invoke strsignal. * emacs.c (shut_down_emacs): Use safe_strsignal, not strsignal. * process.c (status_message): Use const pointer, in case strsignal is #defined to safe_strsignal. * sysdep.c (sys_siglist, init_signals): Always define and initialize a substitute sys_siglist if the system does not define one, even if HAVE_STRSIGNAL. (safe_strsignal): Rename from strsignal. Always define, using sys_siglist. Return a const pointer. * syssignal.h (safe_strsignal): New decl. (strsignal) [!HAVE_STRSIGNAL]: Define in terms of safe_strsignal.
-rw-r--r--src/ChangeLog19
-rw-r--r--src/emacs.c2
-rw-r--r--src/process.c2
-rw-r--r--src/sysdep.c29
-rw-r--r--src/syssignal.h4
5 files changed, 36 insertions, 20 deletions
diff --git a/src/ChangeLog b/src/ChangeLog
index 7061038fdec..3a94af80c30 100644
--- a/src/ChangeLog
+++ b/src/ChangeLog
@@ -1,3 +1,22 @@
+2012-10-01 Paul Eggert <eggert@cs.ucla.edu>
+
+ Fix a malloc race condition involving strsignal.
+ A signal can arrive in the middle of a malloc, and Emacs's signal
+ handler can invoke strsignal, which can invoke malloc, which is
+ not portable. This race condition bug makes Emacs hang on GNU/Linux.
+ Fix it by altering the signal handler so that it does not invoke
+ strsignal.
+ * emacs.c (shut_down_emacs): Use safe_strsignal, not strsignal.
+ * process.c (status_message): Use const pointer, in case strsignal
+ is #defined to safe_strsignal.
+ * sysdep.c (sys_siglist, init_signals): Always define and
+ initialize a substitute sys_siglist if the system does not define
+ one, even if HAVE_STRSIGNAL.
+ (safe_strsignal): Rename from strsignal. Always define,
+ using sys_siglist. Return a const pointer.
+ * syssignal.h (safe_strsignal): New decl.
+ (strsignal) [!HAVE_STRSIGNAL]: Define in terms of safe_strsignal.
+
2012-10-01 Eli Zaretskii <eliz@gnu.org>
* w32proc.c (timer_loop): Fix code that waits for timer
diff --git a/src/emacs.c b/src/emacs.c
index a603a56b792..bc54f56b98a 100644
--- a/src/emacs.c
+++ b/src/emacs.c
@@ -1886,7 +1886,7 @@ shut_down_emacs (int sig, Lisp_Object stuff)
static char const format[] = "Fatal error %d: ";
char buf[sizeof format - 2 + INT_STRLEN_BOUND (int)];
int buflen = sprintf (buf, format, sig);
- char const *sig_desc = strsignal (sig);
+ char const *sig_desc = safe_strsignal (sig);
ignore_value (write (STDERR_FILENO, buf, buflen));
ignore_value (write (STDERR_FILENO, sig_desc, strlen (sig_desc)));
}
diff --git a/src/process.c b/src/process.c
index dfc89809fa5..92bea0d3a27 100644
--- a/src/process.c
+++ b/src/process.c
@@ -570,7 +570,7 @@ status_message (struct Lisp_Process *p)
if (EQ (symbol, Qsignal) || EQ (symbol, Qstop))
{
- char *signame;
+ char const *signame;
synchronize_system_messages_locale ();
signame = strsignal (code);
if (signame == 0)
diff --git a/src/sysdep.c b/src/sysdep.c
index a825145c6dd..74617fcaf0f 100644
--- a/src/sysdep.c
+++ b/src/sysdep.c
@@ -1543,12 +1543,10 @@ deliver_thread_signal (int sig, signal_handler_t handler)
errno = old_errno;
}
-#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
-static char *my_sys_siglist[NSIG];
-# ifdef sys_siglist
-# undef sys_siglist
-# endif
+#if !HAVE_DECL_SYS_SIGLIST
+# undef sys_siglist
# define sys_siglist my_sys_siglist
+static char const *sys_siglist[NSIG];
#endif
/* Handle bus errors, invalid instruction, etc. */
@@ -1611,7 +1609,7 @@ init_signals (bool dumping)
main_thread = pthread_self ();
#endif
-#if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST
+#if !HAVE_DECL_SYS_SIGLIST
if (! initialized)
{
sys_siglist[SIGABRT] = "Aborted";
@@ -1759,7 +1757,7 @@ init_signals (bool dumping)
sys_siglist[SIGXFSZ] = "File size limit exceeded";
# endif
}
-#endif /* !defined HAVE_STRSIGNAL && !defined HAVE_DECL_SYS_SIGLIST */
+#endif /* !HAVE_DECL_SYS_SIGLIST */
/* Don't alter signal handlers if dumping. On some machines,
changing signal handlers sets static data that would make signals
@@ -2280,21 +2278,20 @@ set_file_times (int fd, const char *filename,
return fdutimens (fd, filename, timespec);
}
-#ifndef HAVE_STRSIGNAL
-char *
-strsignal (int code)
+/* Like strsignal, except async-signal-safe, and this function typically
+ returns a string in the C locale rather than the current locale. */
+char const *
+safe_strsignal (int code)
{
- char *signame = 0;
+ char const *signame = 0;
if (0 <= code && code < NSIG)
- {
- /* Cast to suppress warning if the table has const char *. */
- signame = (char *) sys_siglist[code];
- }
+ signame = sys_siglist[code];
+ if (! signame)
+ signame = "Unknown signal";
return signame;
}
-#endif /* HAVE_STRSIGNAL */
#ifndef DOS_NT
/* For make-serial-process */
diff --git a/src/syssignal.h b/src/syssignal.h
index ece2515dec9..66538aad100 100644
--- a/src/syssignal.h
+++ b/src/syssignal.h
@@ -39,6 +39,7 @@ extern sigset_t empty_mask;
typedef void (*signal_handler_t) (int);
extern void emacs_sigaction_init (struct sigaction *, signal_handler_t);
+char const *safe_strsignal (int);
#if NSIG < NSIG_MINIMUM
# undef NSIG
@@ -70,8 +71,7 @@ extern void emacs_sigaction_init (struct sigaction *, signal_handler_t);
#endif /* ! defined (SIGCLD) */
#ifndef HAVE_STRSIGNAL
-/* strsignal is in sysdep.c */
-char *strsignal (int);
+# define strsignal(sig) safe_strsignal (sig)
#endif
void deliver_process_signal (int, signal_handler_t);