From 1f5f3053cd03654f296ad96cfcc185fdc8fe9b10 Mon Sep 17 00:00:00 2001 From: Sami Imseih Date: Thu, 5 Mar 2026 16:13:12 -0600 Subject: [PATCH v2 1/1] Fix DSA pagemap undersizing in make_new_segment When make_new_segment() creates an odd-sized segment, the pagemap is only sized for usable_pages entries. But the segment also contains metadata pages, and the FreePageManager uses absolute page indices that cover the entire segment. Accesses to pagemap entries beyond usable_pages are out of bounds. The normal (geometric) path correctly sizes the pagemap for all pages in the segment. The odd-sized path should do the same, but it works forward from usable_pages rather than backward from total_size. Fix by adding pagemap entries for the metadata pages after the initial metadata_bytes calculation. Use integer ceiling division to compute the exact number of additional entries needed, avoiding iteration. Add an Assert to verify the pagemap covers all pages in the segment. Author: Paul Bunn Reported-by: Paul Bunn --- src/backend/utils/mmgr/dsa.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index ce9ede4c196..222fa86d83d 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -2196,6 +2196,8 @@ make_new_segment(dsa_area *area, size_t requested_pages) /* See if that is enough... */ if (requested_pages > usable_pages) { + size_t total_pages; + /* * We'll make an odd-sized segment, working forward from the requested * number of pages. @@ -2206,10 +2208,28 @@ make_new_segment(dsa_area *area, size_t requested_pages) MAXALIGN(sizeof(FreePageManager)) + usable_pages * sizeof(dsa_pointer); + /* + * We must also account for pagemap entries needed to cover the + * metadata pages themselves. The pagemap must track all pages in the + * segment, including the pages occupied by metadata. + */ + metadata_bytes += + ((metadata_bytes + (FPM_PAGE_SIZE - sizeof(dsa_pointer)) - 1) / + (FPM_PAGE_SIZE - sizeof(dsa_pointer))) * + sizeof(dsa_pointer); + /* Add padding up to next page boundary. */ if (metadata_bytes % FPM_PAGE_SIZE != 0) metadata_bytes += FPM_PAGE_SIZE - (metadata_bytes % FPM_PAGE_SIZE); total_size = metadata_bytes + usable_pages * FPM_PAGE_SIZE; + total_pages = total_size / FPM_PAGE_SIZE; + + /* + * Verify we allocated enough pagemap entries for metadata and usable + * pages + */ + Assert((metadata_bytes - MAXALIGN(sizeof(dsa_segment_header)) - + MAXALIGN(sizeof(FreePageManager))) / sizeof(dsa_pointer) >= total_pages); /* Is that too large for dsa_pointer's addressing scheme? */ if (total_size > DSA_MAX_SEGMENT_SIZE) -- 2.50.1 (Apple Git-155)