Fix memory lifetime issues of replication slot stats.
authorAndres Freund <[email protected]>
Wed, 17 Mar 2021 23:18:37 +0000 (16:18 -0700)
committerAndres Freund <[email protected]>
Wed, 17 Mar 2021 23:21:46 +0000 (16:21 -0700)
When accessing replication slot stats, introduced in 98681675002d,
pgstat_read_statsfiles() reads the data into newly allocated
memory. Unfortunately the current memory context at that point is the
callers, leading to leaks and use-after-free dangers.

The fix is trivial, explicitly use pgStatLocalContext. There's some
potential for further improvements, but that's outside of the scope of
this bugfix.

No backpatch necessary, feature is only in HEAD.

Author: Andres Freund <[email protected]>
Discussion: https://postgr.es/m/20210317230447[email protected]

contrib/test_decoding/expected/stats.out
contrib/test_decoding/sql/stats.sql
src/backend/postmaster/pgstat.c

index dafca965201d0545ff3c4be97a5ebcd534e69b08..bca36fa90309c231750100e444be80309e98bdc4 100644 (file)
@@ -101,6 +101,22 @@ SELECT slot_name, spill_txns > 0 AS spill_txns, spill_count > 0 AS spill_count F
  regression_slot | t          | t
 (1 row)
 
+-- Ensure stats can be repeatedly accessed using the same stats snapshot. See
+-- https://postgr.es/m/20210317230447.c7uc4g3vbs4wi32i%40alap3.anarazel.de
+BEGIN;
+SELECT slot_name FROM pg_stat_replication_slots;
+    slot_name    
+-----------------
+ regression_slot
+(1 row)
+
+SELECT slot_name FROM pg_stat_replication_slots;
+    slot_name    
+-----------------
+ regression_slot
+(1 row)
+
+COMMIT;
 DROP FUNCTION wait_for_decode_stats(bool);
 DROP TABLE stats_test;
 SELECT pg_drop_replication_slot('regression_slot');
index 182df84030d0db28fb3d646b28a37ff31fcf9d76..51294e48e87fd01e547d358dc4885ba801808584 100644 (file)
@@ -59,6 +59,13 @@ SELECT count(*) FROM pg_logical_slot_peek_changes('regression_slot', NULL, NULL,
 SELECT wait_for_decode_stats(false);
 SELECT slot_name, spill_txns > 0 AS spill_txns, spill_count > 0 AS spill_count FROM pg_stat_replication_slots;
 
+-- Ensure stats can be repeatedly accessed using the same stats snapshot. See
+-- https://postgr.es/m/20210317230447.c7uc4g3vbs4wi32i%40alap3.anarazel.de
+BEGIN;
+SELECT slot_name FROM pg_stat_replication_slots;
+SELECT slot_name FROM pg_stat_replication_slots;
+COMMIT;
+
 DROP FUNCTION wait_for_decode_stats(bool);
 DROP TABLE stats_test;
 SELECT pg_drop_replication_slot('regression_slot');
index b1e2d94951d3e0290ee7b407d9e1414dad7a45d7..208a33692f353edbc0e022cdf82ef45ea1004578 100644 (file)
@@ -5568,7 +5568,9 @@ pgstat_read_statsfiles(Oid onlydb, bool permanent, bool deep)
                         HASH_ELEM | HASH_BLOBS | HASH_CONTEXT);
 
    /* Allocate the space for replication slot statistics */
-   replSlotStats = palloc0(max_replication_slots * sizeof(PgStat_ReplSlotStats));
+   replSlotStats = MemoryContextAllocZero(pgStatLocalContext,
+                                          max_replication_slots
+                                          * sizeof(PgStat_ReplSlotStats));
    nReplSlotStats = 0;
 
    /*
@@ -6323,6 +6325,8 @@ pgstat_clear_snapshot(void)
    pgStatDBHash = NULL;
    localBackendStatusTable = NULL;
    localNumBackends = 0;
+   replSlotStats = NULL;
+   nReplSlotStats = 0;
 }