From c161c11875571195600b9fdb13df728116c9e960 Mon Sep 17 00:00:00 2001 From: Weiqi Wang Date: Mon, 12 Oct 2026 09:41:55 +0200 Subject: [PATCH] xen/pdx: fix offset-compression merge of a contained range When sorting and merging overlapping ranges in pfn_pdx_compression_setup(), the merged range is set to end where the second range ends. If the second range is fully contained in the first, this truncates the first range, and the tail of it is then neither compressible nor translated correctly. Keep the end of the merged range as the maximum of both ends. On x86 the ranges come from the SRAT memory affinity entries, and overlapping entries for the same node are tolerated with a warning by the NUMA code. The caller's subsequent coverage check catches the truncated range, so the effect is that PDX compression is disabled with a "RAM region ... not covered" message rather than memory being mistranslated. Add a test case that fails without this change. Found with the ESBMC bounded model checker, and confirmed by running the failing case natively against the unmodified code. Fixes: c5c45bcbd6a1 ("pdx: introduce a new compression algorithm based on region offsets") Assisted-by: Claude Code:claude-opus-5-5 # finding the issue with ESBMC, patch creation Signed-off-by: Weiqi Wang Reviewed-by: Jan Beulich --- tools/tests/pdx/test-pdx.c | 16 ++++++++++++++++ xen/common/pdx.c | 5 +++-- 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/tools/tests/pdx/test-pdx.c b/tools/tests/pdx/test-pdx.c index 4de8d43d86..6728ffc7a5 100644 --- a/tools/tests/pdx/test-pdx.c +++ b/tools/tests/pdx/test-pdx.c @@ -88,6 +88,22 @@ int main(int argc, char **argv) .compress = true, }, #endif + /* Range contained in a previous one. */ + { + .ranges = { + /* Overlapping ranges. */ + { .start = 0, .end = (1UL << MAX_ORDER) * 2 }, + { .start = 0, .end = (1UL << MAX_ORDER) * 1 }, + /* Extra range to force offset compression to be engaged. */ + { .start = (1UL << MAX_ORDER) * 3, + .end = (1UL << MAX_ORDER) * 4 }, + }, +#ifdef CONFIG_PDX_OFFSET_COMPRESSION + .compress = true, +#else + .compress = false, +#endif + }, /* PDX compression, 2 ranges covered by the lower mask. */ { .ranges = { diff --git a/xen/common/pdx.c b/xen/common/pdx.c index e7e16e193e..23655ef3bd 100644 --- a/xen/common/pdx.c +++ b/xen/common/pdx.c @@ -393,8 +393,9 @@ bool __init pfn_pdx_compression_setup(paddr_t base) (ranges[i - 1].base_pfn + ranges[i - 1].pages) ) continue; - ranges[i - 1].pages = ranges[i].base_pfn + ranges[i].pages - - ranges[i - 1].base_pfn; + ranges[i - 1].pages = max(ranges[i - 1].pages, + ranges[i].base_pfn + ranges[i].pages - + ranges[i - 1].base_pfn); if ( i + 1 < nr_ranges ) memmove(&ranges[i], &ranges[i + 1],