diff options
| author | Paul Eggert | 2012-10-01 15:12:44 -0700 |
|---|---|---|
| committer | Paul Eggert | 2012-10-01 15:12:44 -0700 |
| commit | aa1ba90e4a95542c83cf636de3bc67e8fb23bad3 (patch) | |
| tree | 1407a999bbc11bf54aaeba40764d6a75565db182 /src | |
| parent | ace917bddb2ed2448a97ddf279445bb581c5cd32 (diff) | |
| download | emacs-aa1ba90e4a95542c83cf636de3bc67e8fb23bad3.tar.gz emacs-aa1ba90e4a95542c83cf636de3bc67e8fb23bad3.zip | |
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.
Diffstat (limited to 'src')
| -rw-r--r-- | src/ChangeLog | 19 | ||||
| -rw-r--r-- | src/emacs.c | 2 | ||||
| -rw-r--r-- | src/process.c | 2 | ||||
| -rw-r--r-- | src/sysdep.c | 29 | ||||
| -rw-r--r-- | src/syssignal.h | 4 |
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 @@ | |||
| 1 | 2012-10-01 Paul Eggert <eggert@cs.ucla.edu> | ||
| 2 | |||
| 3 | Fix a malloc race condition involving strsignal. | ||
| 4 | A signal can arrive in the middle of a malloc, and Emacs's signal | ||
| 5 | handler can invoke strsignal, which can invoke malloc, which is | ||
| 6 | not portable. This race condition bug makes Emacs hang on GNU/Linux. | ||
| 7 | Fix it by altering the signal handler so that it does not invoke | ||
| 8 | strsignal. | ||
| 9 | * emacs.c (shut_down_emacs): Use safe_strsignal, not strsignal. | ||
| 10 | * process.c (status_message): Use const pointer, in case strsignal | ||
| 11 | is #defined to safe_strsignal. | ||
| 12 | * sysdep.c (sys_siglist, init_signals): Always define and | ||
| 13 | initialize a substitute sys_siglist if the system does not define | ||
| 14 | one, even if HAVE_STRSIGNAL. | ||
| 15 | (safe_strsignal): Rename from strsignal. Always define, | ||
| 16 | using sys_siglist. Return a const pointer. | ||
| 17 | * syssignal.h (safe_strsignal): New decl. | ||
| 18 | (strsignal) [!HAVE_STRSIGNAL]: Define in terms of safe_strsignal. | ||
| 19 | |||
| 1 | 2012-10-01 Eli Zaretskii <eliz@gnu.org> | 20 | 2012-10-01 Eli Zaretskii <eliz@gnu.org> |
| 2 | 21 | ||
| 3 | * w32proc.c (timer_loop): Fix code that waits for timer | 22 | * 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) | |||
| 1886 | static char const format[] = "Fatal error %d: "; | 1886 | static char const format[] = "Fatal error %d: "; |
| 1887 | char buf[sizeof format - 2 + INT_STRLEN_BOUND (int)]; | 1887 | char buf[sizeof format - 2 + INT_STRLEN_BOUND (int)]; |
| 1888 | int buflen = sprintf (buf, format, sig); | 1888 | int buflen = sprintf (buf, format, sig); |
| 1889 | char const *sig_desc = strsignal (sig); | 1889 | char const *sig_desc = safe_strsignal (sig); |
| 1890 | ignore_value (write (STDERR_FILENO, buf, buflen)); | 1890 | ignore_value (write (STDERR_FILENO, buf, buflen)); |
| 1891 | ignore_value (write (STDERR_FILENO, sig_desc, strlen (sig_desc))); | 1891 | ignore_value (write (STDERR_FILENO, sig_desc, strlen (sig_desc))); |
| 1892 | } | 1892 | } |
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) | |||
| 570 | 570 | ||
| 571 | if (EQ (symbol, Qsignal) || EQ (symbol, Qstop)) | 571 | if (EQ (symbol, Qsignal) || EQ (symbol, Qstop)) |
| 572 | { | 572 | { |
| 573 | char *signame; | 573 | char const *signame; |
| 574 | synchronize_system_messages_locale (); | 574 | synchronize_system_messages_locale (); |
| 575 | signame = strsignal (code); | 575 | signame = strsignal (code); |
| 576 | if (signame == 0) | 576 | 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) | |||
| 1543 | errno = old_errno; | 1543 | errno = old_errno; |
| 1544 | } | 1544 | } |
| 1545 | 1545 | ||
| 1546 | #if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST | 1546 | #if !HAVE_DECL_SYS_SIGLIST |
| 1547 | static char *my_sys_siglist[NSIG]; | 1547 | # undef sys_siglist |
| 1548 | # ifdef sys_siglist | ||
| 1549 | # undef sys_siglist | ||
| 1550 | # endif | ||
| 1551 | # define sys_siglist my_sys_siglist | 1548 | # define sys_siglist my_sys_siglist |
| 1549 | static char const *sys_siglist[NSIG]; | ||
| 1552 | #endif | 1550 | #endif |
| 1553 | 1551 | ||
| 1554 | /* Handle bus errors, invalid instruction, etc. */ | 1552 | /* Handle bus errors, invalid instruction, etc. */ |
| @@ -1611,7 +1609,7 @@ init_signals (bool dumping) | |||
| 1611 | main_thread = pthread_self (); | 1609 | main_thread = pthread_self (); |
| 1612 | #endif | 1610 | #endif |
| 1613 | 1611 | ||
| 1614 | #if !defined HAVE_STRSIGNAL && !HAVE_DECL_SYS_SIGLIST | 1612 | #if !HAVE_DECL_SYS_SIGLIST |
| 1615 | if (! initialized) | 1613 | if (! initialized) |
| 1616 | { | 1614 | { |
| 1617 | sys_siglist[SIGABRT] = "Aborted"; | 1615 | sys_siglist[SIGABRT] = "Aborted"; |
| @@ -1759,7 +1757,7 @@ init_signals (bool dumping) | |||
| 1759 | sys_siglist[SIGXFSZ] = "File size limit exceeded"; | 1757 | sys_siglist[SIGXFSZ] = "File size limit exceeded"; |
| 1760 | # endif | 1758 | # endif |
| 1761 | } | 1759 | } |
| 1762 | #endif /* !defined HAVE_STRSIGNAL && !defined HAVE_DECL_SYS_SIGLIST */ | 1760 | #endif /* !HAVE_DECL_SYS_SIGLIST */ |
| 1763 | 1761 | ||
| 1764 | /* Don't alter signal handlers if dumping. On some machines, | 1762 | /* Don't alter signal handlers if dumping. On some machines, |
| 1765 | changing signal handlers sets static data that would make signals | 1763 | changing signal handlers sets static data that would make signals |
| @@ -2280,21 +2278,20 @@ set_file_times (int fd, const char *filename, | |||
| 2280 | return fdutimens (fd, filename, timespec); | 2278 | return fdutimens (fd, filename, timespec); |
| 2281 | } | 2279 | } |
| 2282 | 2280 | ||
| 2283 | #ifndef HAVE_STRSIGNAL | 2281 | /* Like strsignal, except async-signal-safe, and this function typically |
| 2284 | char * | 2282 | returns a string in the C locale rather than the current locale. */ |
| 2285 | strsignal (int code) | 2283 | char const * |
| 2284 | safe_strsignal (int code) | ||
| 2286 | { | 2285 | { |
| 2287 | char *signame = 0; | 2286 | char const *signame = 0; |
| 2288 | 2287 | ||
| 2289 | if (0 <= code && code < NSIG) | 2288 | if (0 <= code && code < NSIG) |
| 2290 | { | 2289 | signame = sys_siglist[code]; |
| 2291 | /* Cast to suppress warning if the table has const char *. */ | 2290 | if (! signame) |
| 2292 | signame = (char *) sys_siglist[code]; | 2291 | signame = "Unknown signal"; |
| 2293 | } | ||
| 2294 | 2292 | ||
| 2295 | return signame; | 2293 | return signame; |
| 2296 | } | 2294 | } |
| 2297 | #endif /* HAVE_STRSIGNAL */ | ||
| 2298 | 2295 | ||
| 2299 | #ifndef DOS_NT | 2296 | #ifndef DOS_NT |
| 2300 | /* For make-serial-process */ | 2297 | /* 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; | |||
| 39 | typedef void (*signal_handler_t) (int); | 39 | typedef void (*signal_handler_t) (int); |
| 40 | 40 | ||
| 41 | extern void emacs_sigaction_init (struct sigaction *, signal_handler_t); | 41 | extern void emacs_sigaction_init (struct sigaction *, signal_handler_t); |
| 42 | char const *safe_strsignal (int); | ||
| 42 | 43 | ||
| 43 | #if NSIG < NSIG_MINIMUM | 44 | #if NSIG < NSIG_MINIMUM |
| 44 | # undef NSIG | 45 | # undef NSIG |
| @@ -70,8 +71,7 @@ extern void emacs_sigaction_init (struct sigaction *, signal_handler_t); | |||
| 70 | #endif /* ! defined (SIGCLD) */ | 71 | #endif /* ! defined (SIGCLD) */ |
| 71 | 72 | ||
| 72 | #ifndef HAVE_STRSIGNAL | 73 | #ifndef HAVE_STRSIGNAL |
| 73 | /* strsignal is in sysdep.c */ | 74 | # define strsignal(sig) safe_strsignal (sig) |
| 74 | char *strsignal (int); | ||
| 75 | #endif | 75 | #endif |
| 76 | 76 | ||
| 77 | void deliver_process_signal (int, signal_handler_t); | 77 | void deliver_process_signal (int, signal_handler_t); |