Fix assorted causes of segmentation fault.
authorTatsuo Ishii <ishii@sraoss.co.jp>
Thu, 4 Apr 2024 04:54:34 +0000 (13:54 +0900)
committerTatsuo Ishii <ishii@sraoss.co.jp>
Thu, 4 Apr 2024 06:56:27 +0000 (15:56 +0900)
It is reported that pgpool and its child process segfault in certain
cases when failover involved.

In pgpool main get_query_result (called from find_primary_node) crashed.

do_query(slots[backend_id]->con, query, res, PROTO_MAJOR_V3);

It seems slots[0] is NULL here. slots[0] is created by
make_persistent_db_connection_noerror() but it failed with log
message: "find_primary_node: make_persistent_db_connection_noerror
failed on node 0". Note that at the time when
make_persistent_db_connection_noerror() is called, VALID_BACKEND
reported that node 0 is up. This means that failover is ongoing and
the node status used by VALID_BACKEND did not catch up. As a result
get_query_user is called with slots[0] = NULL, which caused the
segfault. Fix is, check slots entry before calling
get_query_result.

Also health check has an issue with connection "slot" memory. It is
managed by HealthCheckMemoryContext. slot is the pointer to the
memory. When elog(ERROR) is raised, pgpool long jumps and resets the
memory context. Thus, slot remains as a pointer to freed memory. To
fix this, always set NULL to slot right after the
HealthCheckMemoryContext call.

Similar issue is found with streaming replication check too and is
also fixed in this commit.

Problem reported and analyzed: Emond Papegaaij
Backpatch-through: v4.4
Discussion:
[pgpool-general: 9070] Re: Segmentation after switchover
https://www.pgpool.net/pipermail/pgpool-general/2024-April/009131.html

src/main/health_check.c
src/main/pgpool_main.c
src/streaming_replication/pool_worker_child.c

index 17a94d86ff42b580079b05db3113635c020bcd57..fec0e6dbc142e12056d72c6e2e1761f0ba77f289 100644 (file)
@@ -5,7 +5,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2022     PgPool Global Development Group
+ * Copyright (c) 2003-2024     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -172,6 +172,12 @@ do_health_check_child(int *node_id)
        {
                MemoryContextSwitchTo(HealthCheckMemoryContext);
                MemoryContextResetAndDeleteChildren(HealthCheckMemoryContext);
+               /*
+                * Since HealthCheckMemoryContext is used for "slot", we need to clear it
+                * so that new slot is allocated later on.
+                */
+               slot = NULL;
+
                bool    skipped = false;
 
                CHECK_REQUEST;
index edd8a3094066acc4d5973820f93fb8d569723e4a..ea4c489e52a48ad1a9d484fd5a10594777eaa2ca 100644 (file)
@@ -5,7 +5,7 @@
  * pgpool: a language independent connection pool server for PostgreSQL
  * written by Tatsuo Ishii
  *
- * Copyright (c) 2003-2023     PgPool Global Development Group
+ * Copyright (c) 2003-2024     PgPool Global Development Group
  *
  * Permission to use, copy, modify, and distribute this software and
  * its documentation for any purpose and without fee is hereby
@@ -2601,7 +2601,7 @@ verify_backend_node_status(POOL_CONNECTION_POOL_SLOT * *slots)
 
                                for (j = 0; j < NUM_BACKENDS; j++)
                                {
-                                       if (pool_node_status[j] == POOL_NODE_STATUS_STANDBY)
+                                       if (pool_node_status[j] == POOL_NODE_STATUS_STANDBY && slots[j])
                                        {
                                                ereport(DEBUG1,
                                                                (errmsg("verify_backend_node_status: %d is standby", j)));
index 3b1d9cff69b9193cc06c2c28c8cf0129204c8125..a9695fb83baf8f24c30973d136e85f8991833b89 100644 (file)
@@ -162,6 +162,12 @@ do_worker_child(void)
        {
                MemoryContextSwitchTo(WorkerMemoryContext);
                MemoryContextResetAndDeleteChildren(WorkerMemoryContext);
+               /*
+                * Since WorkerMemoryContext is used for "slots", we need to clear it
+                * so that new slots are allocated later on.
+                */
+               memset(slots, 0, sizeof(slots));
+               
                bool    watchdog_leader;        /* true if I am the watchdog leader */