From 5f79580ad69f6e696365bdc63bc265f45bd77211 Mon Sep 17 00:00:00 2001 From: Andres Freund Date: Wed, 17 Mar 2021 16:18:37 -0700 Subject: [PATCH] Fix memory lifetime issues of replication slot stats. 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 Discussion: https://postgr.es/m/20210317230447.c7uc4g3vbs4wi32i@alap3.anarazel.de --- contrib/test_decoding/expected/stats.out | 16 ++++++++++++++++ contrib/test_decoding/sql/stats.sql | 7 +++++++ src/backend/postmaster/pgstat.c | 6 +++++- 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/contrib/test_decoding/expected/stats.out b/contrib/test_decoding/expected/stats.out index dafca96520..bca36fa903 100644 --- a/contrib/test_decoding/expected/stats.out +++ b/contrib/test_decoding/expected/stats.out @@ -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'); diff --git a/contrib/test_decoding/sql/stats.sql b/contrib/test_decoding/sql/stats.sql index 182df84030..51294e48e8 100644 --- a/contrib/test_decoding/sql/stats.sql +++ b/contrib/test_decoding/sql/stats.sql @@ -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'); diff --git a/src/backend/postmaster/pgstat.c b/src/backend/postmaster/pgstat.c index b1e2d94951..208a33692f 100644 --- a/src/backend/postmaster/pgstat.c +++ b/src/backend/postmaster/pgstat.c @@ -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; }