Avoid calling gettext() in signal handlers.

It seems highly unlikely that gettext() can be relied on to be
async-signal-safe.  psql used to understand that, but someone got
it wrong long ago in the src/bin/scripts/ version of handle_sigint,
and then the bad idea was perpetuated when those two versions were
unified into src/fe_utils/cancel.c.

I'm unsure why there have not been field complaints about this
... maybe gettext() is signal-safe once it's translated at least
one message?  But we have no business assuming any such thing.

In cancel.c (v13 and up), I preserved our ability to localize
"Cancel request sent" messages by invoking gettext() before
the signal handler is set up.  In earlier branches I just made
src/bin/scripts/ not localize those messages, as psql did then.

(Just for extra unsafety, the src/bin/scripts/ version was
invoking fprintf() from a signal handler.  Sigh.)

Noted while fixing signal-safety issues in PQcancel() itself.
Back-patch to all supported branches.

Discussion: https://postgr.es/m/2937814.1641960929@sss.pgh.pa.us
This commit is contained in:
Tom Lane 2022-01-17 13:30:04 -05:00
parent 38f099ef93
commit 6d1a854c15

View File

@ -23,6 +23,19 @@
#include "fe_utils/string_utils.h" #include "fe_utils/string_utils.h"
/*
* Write a simple string to stderr --- must be safe in a signal handler.
* We ignore the write() result since there's not much we could do about it.
* Certain compilers make that harder than it ought to be.
*/
#define write_stderr(str) \
do { \
const char *str_ = (str); \
int rc_; \
rc_ = write(fileno(stderr), str_, strlen(str_)); \
(void) rc_; \
} while (0)
#define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e)) #define PQmblenBounded(s, e) strnlen(s, PQmblen(s, e))
static PGcancel *volatile cancelConn = NULL; static PGcancel *volatile cancelConn = NULL;
@ -485,10 +498,13 @@ handle_sigint(SIGNAL_ARGS)
if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
{ {
CancelRequested = true; CancelRequested = true;
fprintf(stderr, _("Cancel request sent\n")); write_stderr("Cancel request sent\n");
} }
else else
fprintf(stderr, _("Could not send cancel request: %s"), errbuf); {
write_stderr("Could not send cancel request: ");
write_stderr(errbuf);
}
} }
else else
CancelRequested = true; CancelRequested = true;
@ -522,11 +538,14 @@ consoleHandler(DWORD dwCtrlType)
{ {
if (PQcancel(cancelConn, errbuf, sizeof(errbuf))) if (PQcancel(cancelConn, errbuf, sizeof(errbuf)))
{ {
fprintf(stderr, _("Cancel request sent\n"));
CancelRequested = true; CancelRequested = true;
write_stderr("Cancel request sent\n");
} }
else else
fprintf(stderr, _("Could not send cancel request: %s"), errbuf); {
write_stderr("Could not send cancel request: ");
write_stderr(errbuf);
}
} }
else else
CancelRequested = true; CancelRequested = true;