From bc7c32cdf43504651e66c88eac8888e9dd232dca Mon Sep 17 00:00:00 2001 From: Sergey Fedorov Date: Sat, 1 Aug 2026 06:43:46 +0000 Subject: [PATCH] xquartz: xpr: fix window id truncation on LP64 (ppc64) xprCreateFrame() passed &pFrame->wid, a (void *), straight to xp_create_window() through a (xp_window_id *) cast. xp_window_id is 32 bits, so only half of the 64-bit RootlessFrameID word was written; the other half was left uninitialized. window_hash is created with x_hash_table_new(NULL, NULL, NULL, NULL), so keys are compared by exact pointer identity -- a dirty high word therefore makes subsequent lookups miss, and also trips the assert(sv == uv); in x_cvt_vptr_to_uint(). On a big-endian LP64 target (ppc64) it is worse still: the 32-bit id is written into the high half of the word, so the truncating conversions used everywhere else (MAKE_WINDOW_ID(), x_cvt_vptr_to_uint()) read the wrong half entirely. This is invisible on ILP32 (32-bit ppc/i386), where void * and unsigned int are the same width and the raw store and the converted value have identical bit patterns -- which is why 10.6/ppc worked while 10.5/ppc64 crashed launching a client. Write the id through a real xp_window_id and zero-extend it into pFrame->wid, so the word is fully defined regardless of ABI or byte order, and key window_hash consistently in both the libdispatch and non-libdispatch paths. With pFrame->wid clean at its single point of origin, the downstream MAKE_WINDOW_ID()/x_cvt_vptr_to_uint() truncations are correct and the assert no longer needs to be disabled on ppc64. --- hw/xquartz/xpr/xprFrame.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/hw/xquartz/xpr/xprFrame.c b/hw/xquartz/xpr/xprFrame.c index b9457b70f..fdf095b0b 100644 --- a/hw/xquartz/xpr/xprFrame.c +++ b/hw/xquartz/xpr/xprFrame.c @@ -210,23 +210,35 @@ xprCreateFrame(RootlessWindowPtr pFrame, ScreenPtr pScreen, wc.window_level = rooted_window_levels[pFrame->level]; mask |= XP_WINDOW_LEVEL; - err = xp_create_window(mask, &wc, (xp_window_id *)&pFrame->wid); + /* xp_create_window() writes a 32-bit xp_window_id. Writing it straight + * into the 64-bit (void *) pFrame->wid via a truncating cast pointer + * leaves the other half of the word uninitialized, and on a big-endian + * LP64 target (ppc64) even lands the id in the wrong half. The value is + * later stored into / looked up from window_hash, which compares keys by + * exact pointer identity, so a dirty high word makes lookups miss and + * trips the assert() in x_cvt_vptr_to_uint(). Go through a real + * xp_window_id and zero-extend it so pFrame->wid is well-defined on every + * ABI and endianness. */ + xp_window_id wid = 0; + err = xp_create_window(mask, &wc, &wid); if (err != Success) { return FALSE; } + pFrame->wid = x_cvt_uint_to_vptr(wid); + #ifdef HAS_LIBDISPATCH WindowHashInsertCtx *ctx = malloc(sizeof(WindowHashInsertCtx)); if (!ctx) return FALSE; - ctx->wid = x_cvt_vptr_to_uint(pFrame->wid); + ctx->wid = wid; ctx->frame = pFrame; dispatch_async_f(window_hash_serial_q, ctx, windowHashInsert); #else pthread_rwlock_wrlock(&window_hash_rwlock); - x_hash_table_insert(window_hash, pFrame->wid, pFrame); + x_hash_table_insert(window_hash, x_cvt_uint_to_vptr(wid), pFrame); pthread_rwlock_unlock(&window_hash_rwlock); #endif -- 2.43.0