From ba8adab91935d18982bcf94d4e142beb79dc6be4 Mon Sep 17 00:00:00 2001 From: Palak Chaturvedi Date: Wed, 5 Aug 2026 14:53:38 +0000 Subject: [PATCH] test_shmem: make resizable shmem tests portable Fix several portability and synchronization problems in the resizable shared memory test. test_shmem_usage() followed every address-adjacent VMA, so any private mapping that happened to abut the main shared-memory segment could be added to the shared-memory usage: a glibc arena on Linux 64-bit, the writable segments of loaded libraries on 32-bit, an AddressSanitizer shadow region, and so on. Continue across split VMAs only while they remain contiguous and retain the target mapping's shared/private flag. This still accounts for the protected reservation tail and can detect pages that were not released while shrinking. Use long for values read with the %ld conversion specifier. This avoids a type mismatch on MSVC, where long is 32 bits. Also read beyond the current structure size through a volatile pointer, so the compiler cannot fold the faulting loop to one load. After crashing a backend, wait for the post-reinitialization ready message in the server log. Polling SELECT 1 can succeed before the postmaster has processed SIGCHLD and reinitialized shared memory. Skip the resizable shared memory test on Windows. Windows CI runs with an Administrator token, which direct postgres -C execution refuses, and the /proc/self/smaps accounting the test relies on is Linux-only. --- .../t/002_resizable_shmem_struct.pl | 13 ++++- src/test/modules/test_shmem/test_shmem.c | 50 +++++++++---------- 2 files changed, 35 insertions(+), 28 deletions(-) diff --git a/src/test/modules/test_shmem/t/002_resizable_shmem_struct.pl b/src/test/modules/test_shmem/t/002_resizable_shmem_struct.pl index 4d2fd3c4282..71e49ce455c 100644 --- a/src/test/modules/test_shmem/t/002_resizable_shmem_struct.pl +++ b/src/test/modules/test_shmem/t/002_resizable_shmem_struct.pl @@ -7,6 +7,11 @@ use PostgreSQL::Test::Cluster; use PostgreSQL::Test::Utils; use Test::More; +if ($windows_os) +{ + plan skip_all => 'resizable shared memory tests are not supported on Windows'; +} + # Test resizable shared memory functionality, both when loaded at startup via # shared_preload_libraries and when loaded after startup (late allocation). @@ -85,6 +90,10 @@ sub test_fault_beyond_size for my $mode ('write', 'read') { + # Wait for crash recovery, not just a connection accepted before the + # postmaster has processed SIGCHLD for the crashed backend. + my $log_offset = -s $node->logfile; + my ($ret, $stdout, $stderr) = $node->psql('postgres', "SELECT resizable_shmem_access_beyond_size('$mode');"); ok($ret != 0, "$prefix: $mode past current size crashes the backend"); @@ -92,8 +101,8 @@ sub test_fault_beyond_size qr/server closed the connection unexpectedly|connection to server was lost/, "$prefix: $mode crash reports lost connection"); - $node->poll_query_until('postgres', 'SELECT 1', '1') - or die "server did not come back after $mode crash"; + $node->wait_for_log(qr/database system is ready to accept connections/, + $log_offset); } is($node->safe_psql('postgres', diff --git a/src/test/modules/test_shmem/test_shmem.c b/src/test/modules/test_shmem/test_shmem.c index 83f8ea9cc3c..2100e33f545 100644 --- a/src/test/modules/test_shmem/test_shmem.c +++ b/src/test/modules/test_shmem/test_shmem.c @@ -345,11 +345,8 @@ resizable_shmem_read(PG_FUNCTION_ARGS) * backend. * * The VMA containing our resizable_shmem pointer identifies the start of the - * main shared-memory segment. - * - * mprotect() calls issued when the resizable structure grows and shrinks can - * split the original mmap into several adjacent VMAs, so we sum the accounting - * fields across the base VMA and any VMAs contiguous with it. + * main shared-memory segment. mprotect() can split that mapping into adjacent + * VMAs, so include adjacent VMAs with the same shared/private flag. */ PG_FUNCTION_INFO_V1(test_shmem_usage); Datum @@ -359,12 +356,13 @@ test_shmem_usage(PG_FUNCTION_ARGS) char line[256]; uintptr_t target = (uintptr_t) resizable_shmem; bool in_target_vma = false; + bool target_is_shared = false; bool use_hugetlb = (huge_pages_status == HUGE_PAGES_ON); unsigned long prev_end = 0; - int64 total_rss_kb = 0; - int64 total_swap_kb = 0; - int64 total_shared_hugetlb_kb = 0; - int64 val; + long total_rss_kb = 0; + long total_swap_kb = 0; + long total_shared_hugetlb_kb = 0; + long val; size_t result; f = AllocateFile("/proc/self/smaps", "r"); @@ -377,21 +375,20 @@ test_shmem_usage(PG_FUNCTION_ARGS) { unsigned long start; unsigned long end; + char perms[8]; - if (sscanf(line, "%lx-%lx", &start, &end) == 2) + if (sscanf(line, "%lx-%lx %7s", &start, &end, perms) == 3) { if (in_target_vma) { - /* - * Continue accumulating only across VMAs that are contiguous - * with the previous one; stop as soon as we hit a gap or a - * different mapping. - */ - if (start != prev_end) + if (start != prev_end || (perms[3] == 's') != target_is_shared) break; } - else - in_target_vma = (target >= start && target < end); + else if (target >= start && target < end) + { + in_target_vma = true; + target_is_shared = (perms[3] == 's'); + } prev_end = end; } @@ -482,18 +479,19 @@ resizable_shmem_access_beyond_size(PG_FUNCTION_ARGS) ShmemProtectStruct("resizable_shmem"); #endif - for (int i = resizable_shmem->num_entries; i < test_max_entries; i++) + if (do_write) { - if (do_write) + for (int i = resizable_shmem->num_entries; i < test_max_entries; i++) resizable_shmem->data[i] = 0xdead; - else - sink = resizable_shmem->data[i]; + } + else + { + volatile int32 *volatile_data = resizable_shmem->data; + + for (int i = resizable_shmem->num_entries; i < test_max_entries; i++) + sink = volatile_data[i]; } - /* - * Return the last read value so that compiler doesn't optimize away the - * assignment to sink. - */ PG_RETURN_INT32(sink); } -- 2.43.0