From c123eb70ec6a31cca579f8fd397e2a4ab61dd713 Mon Sep 17 00:00:00 2001 From: wangxiaoran Date: Mon, 19 Aug 2024 11:44:00 +0800 Subject: [PATCH 1/2] Imporve pg_re_throw: check if sigjmp_buf is valid and report error If the code in PG_TRY contains any non local control flow other than ereport(ERROR) like goto, break etc., the PG_CATCH or PG_END_TRY cannot be called, then the PG_exception_stack will point to the memory whose stack frame has been released. So after that, when the pg_re_throw called, __longjmp() will crash and report Segmentation fault error. In that case, to help developers to figure out the root cause easily, it is better to report that 'the sigjmp_buf is invalid' rather than letting the __longjmp report any error. Addition to sigjmp_buf, add another field 'int magic' which is next to the sigjum_buf in the local stack frame memory. The magic's value is always 'PG_exception_magic 0x12345678'. And in 'pg_re_throw' routine, check if the magic's value is still '0x12345678', if not, that means the memory where the 'PG_exception_stack' points to has been released, and the 'sigbuf' must be invalid. --- src/backend/postmaster/autovacuum.c | 8 ++++---- src/backend/postmaster/bgworker.c | 4 ++-- src/backend/postmaster/bgwriter.c | 4 ++-- src/backend/postmaster/checkpointer.c | 5 ++--- src/backend/postmaster/pgarch.c | 4 ++-- src/backend/postmaster/walsummarizer.c | 4 ++-- src/backend/postmaster/walwriter.c | 4 ++-- src/backend/replication/logical/slotsync.c | 4 ++-- src/backend/tcop/postgres.c | 5 ++--- src/backend/utils/error/elog.c | 10 ++++++++-- src/include/utils/elog.h | 14 ++++++++++---- 11 files changed, 38 insertions(+), 28 deletions(-) diff --git a/src/backend/postmaster/autovacuum.c b/src/backend/postmaster/autovacuum.c index 7d0877c95e..4076d99938 100644 --- a/src/backend/postmaster/autovacuum.c +++ b/src/backend/postmaster/autovacuum.c @@ -361,7 +361,7 @@ static void avl_sigusr2_handler(SIGNAL_ARGS); void AutoVacLauncherMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; Assert(startup_data_len == 0); @@ -436,7 +436,7 @@ AutoVacLauncherMain(char *startup_data, size_t startup_data_len) * call redundant, but it is not since InterruptPending might be set * already. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; @@ -1359,7 +1359,7 @@ avl_sigusr2_handler(SIGNAL_ARGS) void AutoVacWorkerMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; Oid dbid; Assert(startup_data_len == 0); @@ -1421,7 +1421,7 @@ AutoVacWorkerMain(char *startup_data, size_t startup_data_len) * seem that this policy makes the HOLD_INTERRUPTS() call redundant, but * it is not since InterruptPending might be set already. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/postmaster/bgworker.c b/src/backend/postmaster/bgworker.c index b83967cda3..d9cb01cd3a 100644 --- a/src/backend/postmaster/bgworker.c +++ b/src/backend/postmaster/bgworker.c @@ -716,7 +716,7 @@ bgworker_die(SIGNAL_ARGS) void BackgroundWorkerMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; BackgroundWorker *worker; bgworker_main_type entrypt; @@ -781,7 +781,7 @@ BackgroundWorkerMain(char *startup_data, size_t startup_data_len) * * We just need to clean up, report the error, and go away. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/postmaster/bgwriter.c b/src/backend/postmaster/bgwriter.c index 0f75548759..445b56ac76 100644 --- a/src/backend/postmaster/bgwriter.c +++ b/src/backend/postmaster/bgwriter.c @@ -86,7 +86,7 @@ static XLogRecPtr last_snapshot_lsn = InvalidXLogRecPtr; void BackgroundWriterMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; MemoryContext bgwriter_context; bool prev_hibernate; WritebackContext wb_context; @@ -150,7 +150,7 @@ BackgroundWriterMain(char *startup_data, size_t startup_data_len) * call redundant, but it is not since InterruptPending might be set * already. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/postmaster/checkpointer.c b/src/backend/postmaster/checkpointer.c index 199f008bcd..e2a2c7336e 100644 --- a/src/backend/postmaster/checkpointer.c +++ b/src/backend/postmaster/checkpointer.c @@ -172,7 +172,7 @@ static void ReqCheckpointHandler(SIGNAL_ARGS); void CheckpointerMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; MemoryContext checkpointer_context; Assert(startup_data_len == 0); @@ -248,7 +248,7 @@ CheckpointerMain(char *startup_data, size_t startup_data_len) * call redundant, but it is not since InterruptPending might be set * already. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; @@ -311,7 +311,6 @@ CheckpointerMain(char *startup_data, size_t startup_data_len) /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; - /* * Unblock signals (they were blocked when the postmaster forked us) */ diff --git a/src/backend/postmaster/pgarch.c b/src/backend/postmaster/pgarch.c index 02f91431f5..3a40334bc4 100644 --- a/src/backend/postmaster/pgarch.c +++ b/src/backend/postmaster/pgarch.c @@ -515,7 +515,7 @@ pgarch_ArchiverCopyLoop(void) static bool pgarch_archiveXlog(char *xlog) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; MemoryContext oldcontext; char pathname[MAXPGPATH]; char activitymsg[MAXFNAMELEN + 16]; @@ -544,7 +544,7 @@ pgarch_archiveXlog(char *xlog) * PgArchiverMain() and use PG_TRY/PG_CATCH here, but the extra code to * avoid the odd archiver restart doesn't seem worth it. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/postmaster/walsummarizer.c b/src/backend/postmaster/walsummarizer.c index daa7909382..da58b26aa8 100644 --- a/src/backend/postmaster/walsummarizer.c +++ b/src/backend/postmaster/walsummarizer.c @@ -210,7 +210,7 @@ WalSummarizerShmemInit(void) void WalSummarizerMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; MemoryContext context; /* @@ -273,7 +273,7 @@ WalSummarizerMain(char *startup_data, size_t startup_data_len) /* * If an exception is encountered, processing resumes here. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/postmaster/walwriter.c b/src/backend/postmaster/walwriter.c index 6e7918a78d..c18e7eb024 100644 --- a/src/backend/postmaster/walwriter.c +++ b/src/backend/postmaster/walwriter.c @@ -88,7 +88,7 @@ int WalWriterFlushAfter = DEFAULT_WAL_WRITER_FLUSH_AFTER; void WalWriterMain(char *startup_data, size_t startup_data_len) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; MemoryContext walwriter_context; int left_till_hibernate; bool hibernating; @@ -147,7 +147,7 @@ WalWriterMain(char *startup_data, size_t startup_data_len) * call redundant, but it is not since InterruptPending might be set * already. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* Since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/replication/logical/slotsync.c b/src/backend/replication/logical/slotsync.c index 2d1914ce08..c35c54edab 100644 --- a/src/backend/replication/logical/slotsync.c +++ b/src/backend/replication/logical/slotsync.c @@ -1333,7 +1333,7 @@ ReplSlotSyncWorkerMain(char *startup_data, size_t startup_data_len) WalReceiverConn *wrconn = NULL; char *dbname; char *err; - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; StringInfoData app_name; Assert(startup_data_len == 0); @@ -1366,7 +1366,7 @@ ReplSlotSyncWorkerMain(char *startup_data, size_t startup_data_len) * operates at the bottom of the exception stack, ERRORs turn into FATALs. * Therefore, we create our own exception handler to catch ERRORs. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* since not using PG_TRY, must reset error stack by hand */ error_context_stack = NULL; diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 8bc6bea113..c2108ef72e 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -4220,7 +4220,7 @@ PostgresSingleUserMain(int argc, char *argv[], void PostgresMain(const char *dbname, const char *username) { - sigjmp_buf local_sigjmp_buf; + PG_exception local_sigjmp_buf = {PG_exception_magic}; /* these must be volatile to ensure state is preserved across longjmp: */ volatile bool send_ready_for_query = true; @@ -4422,7 +4422,7 @@ PostgresMain(const char *dbname, const char *username) * were inside a transaction. */ - if (sigsetjmp(local_sigjmp_buf, 1) != 0) + if (sigsetjmp(local_sigjmp_buf.buf, 1) != 0) { /* * NOTE: if you are tempted to add more code in this if-block, @@ -4537,7 +4537,6 @@ PostgresMain(const char *dbname, const char *username) /* We can now handle ereport(ERROR) */ PG_exception_stack = &local_sigjmp_buf; - if (!ignore_till_sync) send_ready_for_query = true; /* initially, or after error */ diff --git a/src/backend/utils/error/elog.c b/src/backend/utils/error/elog.c index 943d8588f3..b06836feb0 100644 --- a/src/backend/utils/error/elog.c +++ b/src/backend/utils/error/elog.c @@ -93,7 +93,7 @@ /* Global variables */ ErrorContextCallback *error_context_stack = NULL; -sigjmp_buf *PG_exception_stack = NULL; +PG_exception *PG_exception_stack = NULL; /* * Hook for intercepting messages before they are sent to the server log. @@ -1985,7 +1985,13 @@ pg_re_throw(void) { /* If possible, throw the error to the next outer setjmp handler */ if (PG_exception_stack != NULL) - siglongjmp(*PG_exception_stack, 1); + { + if (PG_exception_stack->magic != PG_exception_magic) + ereport(FATAL, + (errmsg("Invalid sigjum_buf, code in PG_TRY cannot contain" + " any non local control flow other than ereport"))); + siglongjmp(PG_exception_stack->buf, 1); + } else { /* diff --git a/src/include/utils/elog.h b/src/include/utils/elog.h index 054dd2bf62..d8dc45c1ff 100644 --- a/src/include/utils/elog.h +++ b/src/include/utils/elog.h @@ -383,11 +383,11 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack; */ #define PG_TRY(...) \ do { \ - sigjmp_buf *_save_exception_stack##__VA_ARGS__ = PG_exception_stack; \ + PG_exception *_save_exception_stack##__VA_ARGS__ = PG_exception_stack; \ ErrorContextCallback *_save_context_stack##__VA_ARGS__ = error_context_stack; \ - sigjmp_buf _local_sigjmp_buf##__VA_ARGS__; \ + PG_exception _local_sigjmp_buf##__VA_ARGS__ = {PG_exception_magic}; \ bool _do_rethrow##__VA_ARGS__ = false; \ - if (sigsetjmp(_local_sigjmp_buf##__VA_ARGS__, 0) == 0) \ + if (sigsetjmp(_local_sigjmp_buf##__VA_ARGS__.buf, 0) == 0) \ { \ PG_exception_stack = &_local_sigjmp_buf##__VA_ARGS__ @@ -426,7 +426,13 @@ extern PGDLLIMPORT ErrorContextCallback *error_context_stack; (pg_re_throw(), pg_unreachable()) #endif -extern PGDLLIMPORT sigjmp_buf *PG_exception_stack; +#define PG_exception_magic 0x12345678 +typedef struct PG_exception +{ + int magic; + sigjmp_buf buf; +} PG_exception; +extern PGDLLIMPORT PG_exception *PG_exception_stack; /* Stuff that error handlers might want to use */ -- 2.39.3 (Apple Git-146)