From 032ecd53ebc31d567ff28643a3083fe5405e38fd Mon Sep 17 00:00:00 2001 From: Mats Kindahl Date: Tue, 24 May 2022 11:02:38 +0200 Subject: Add signal-safe function to write to log Any functions that use `malloc` directly or indirectly are not signal-safe, and `vfprintf` is not signal-safe but used from inside `bgworker_die`. This can cause the system to end up in a deadlock and not respond to signals. This commit adds a signal-safe version of write_stderr() and uses it from signal handlers rather than write_stderr(). --- src/backend/postmaster/bgworker.c | 16 ++++++++++++---- src/backend/utils/error/elog.c | 21 ++++++++++++++++----- src/include/utils/elog.h | 6 ++++++ 3 files changed, 34 insertions(+), 9 deletions(-) diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c index 30682b63b3..3ad44d303b 100644 --- a/src/backend/postmaster/bgworker.c +++ b/src/backend/postmaster/bgworker.c @@ -719,16 +719,24 @@ SanityCheckBackgroundWorker(BackgroundWorker *worker, int elevel) /* * Standard SIGTERM handler for background workers + * + * Only use signal-safe function in signal handler, in particular, ereport() + * and and any printf()-like functions since they directly or indirectly use + * malloc(). */ static void bgworker_die(SIGNAL_ARGS) { PG_SETMASK(&BlockSig); + char message[256] = {0}; + char* ptr = message; + + ptr = stpncpy(ptr, "terminating background worker \"", sizeof(message) - (message - ptr)); + ptr = stpncpy(ptr, MyBgworkerEntry->bgw_type, sizeof(message) - (message - ptr)); + ptr = stpncpy(ptr, "\" due to administrator command", sizeof(message) - (message - ptr)); - ereport(FATAL, - (errcode(ERRCODE_ADMIN_SHUTDOWN), - errmsg("terminating background worker \"%s\" due to administrator command", - MyBgworkerEntry->bgw_type))); + signal_safe_write_stderr(message, strlen(message)); + die(postgres_signal_arg); } /* diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 55ee5423af..d8c94ba87f 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -54,11 +54,11 @@ */ #include "postgres.h" +#include #include +#include #include #include -#include -#include #ifdef HAVE_SYSLOG #include #endif @@ -3384,23 +3384,34 @@ write_stderr(const char *fmt,...) fflush(stderr); #else vsnprintf(errbuf, sizeof(errbuf), fmt, ap); + signal_safe_write_stderr(errbuf, strlen(errbuf)); +#endif + va_end(ap); +} + +void +signal_safe_write_stderr(const char *errbuf, size_t buflen) +{ +#ifndef WIN32 + /* If this fails, there is not much we can do, so ignore the error */ + (void) write(fileno(stderr), errbuf, buflen); +#else /* * On Win32, we print to stderr if running on a console, or write to * eventlog if running as a service */ if (pgwin32_is_service()) /* Running as a service */ { - write_eventlog(ERROR, errbuf, strlen(errbuf)); + write_eventlog(ERROR, errbuf, buflen); } else { /* Not running as service, write to stderr */ - write_console(errbuf, strlen(errbuf)); + write_console(errbuf, buflen); fflush(stderr); } #endif - va_end(ap); } diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index f5c6cd904d..d6ed4e9c03 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -467,4 +467,10 @@ extern void set_syslog_parameters(const char *ident, int facility); */ extern void write_stderr(const char *fmt,...) pg_attribute_printf(1, 2); +/* + * Signal-safe function to write error messages. Can be used from inside + * signal-handlers. + */ +extern void signal_safe_write_stderr(const char* errbuf, size_t buflen); + #endif /* ELOG_H */ -- 2.25.1