From ab50450d2de57d5befd466e2344c342dbf9dd33e Mon Sep 17 00:00:00 2001 From: Paul Bunn Date: Wed, 4 Mar 2026 23:09:36 -0800 Subject: [PATCH] Fix DSA pagemap undersizing in make_new_segment When make_new_segment creates an "odd-sized" segment (the path taken when the requested allocation exceeds the geometric segment size), the pagemap is sized for usable_pages entries. However, the FreePageManager hands out pages using absolute page indices that range up to metadata_pages + usable_pages - 1. Since metadata_pages >= 1 always, the last metadata_pages page indices exceed the pagemap array bounds. The normal (geometric) path correctly sizes the pagemap for total_pages (= total_size / FPM_PAGE_SIZE), which includes both metadata and usable pages. The odd-sized path should do the same, but it works forward from usable_pages rather than backward from total_size, creating a circular dependency: metadata_bytes depends on total_pages which depends on metadata_bytes. This can be resolved in closed form. After computing metadata_bytes for usable_pages entries, add pagemap entries for the metadata pages themselves. The divisor (FPM_PAGE_SIZE - sizeof(dsa_pointer)) accounts for the fact that each metadata page consumes one pagemap entry (of sizeof(dsa_pointer) bytes), leaving only (FPM_PAGE_SIZE - sizeof(dsa_pointer)) net bytes per metadata page. The +1 absorbs the ceiling. This yields the exact metadata_pages in a single expression with no iteration needed. The bug is observable when a parallel hash join (or any operation) makes a DSA allocation large enough to trigger the odd-sized segment path. During subsequent reuse of the segment, allocations landing on pages with indices >= usable_pages cause out-of-bounds pagemap reads/writes. On write, span pointers are stored into the data area, corrupting allocated objects. On read (during dsa_free), garbage is interpreted as a span pointer, typically crashing in dsa_get_address with "dsa_area could not attach to segment" or SIGSEGV. The bug has been present since the DSA implementation was introduced and affects all supported versions. --- src/backend/utils/mmgr/dsa.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/backend/utils/mmgr/dsa.c b/src/backend/utils/mmgr/dsa.c index ce9ede4..789751f 100644 --- a/src/backend/utils/mmgr/dsa.c +++ b/src/backend/utils/mmgr/dsa.c @@ -2206,6 +2206,11 @@ make_new_segment(dsa_area *area, size_t requested_pages) MAXALIGN(sizeof(FreePageManager)) + usable_pages * sizeof(dsa_pointer); + /* Also cover the metadata pages in the pagemap (+1 for rounding). */ + metadata_bytes += + ((metadata_bytes / (FPM_PAGE_SIZE - sizeof(dsa_pointer))) + 1) * + 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); -- 2.43.7