Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

x86/fpu/xstate: Adjust XSAVE buffer size calculation

The current xstate size calculation assumes that the highest-numbered
xstate feature has the highest offset in the buffer, determining the size
based on the topmost bit in the feature mask. However, this assumption is
not architecturally guaranteed -- higher-numbered features may have lower
offsets.

With the introduction of the xfeature order table and its helper macro,
xstate components can now be traversed in their positional order. Update
the non-compacted format handling to iterate through the table to
determine the last-positioned feature. Then, set the offset accordingly.

Since size calculation primarily occurs during initialization or in
non-critical paths, looping to find the last feature is not expected to
have a meaningful performance impact.

Signed-off-by: Chang S. Bae <chang.seok.bae@intel.com>
Signed-off-by: Ingo Molnar <mingo@kernel.org>
Cc: Andy Lutomirski <luto@kernel.org>
Cc: H. Peter Anvin <hpa@zytor.com>
Cc: Linus Torvalds <torvalds@linux-foundation.org>
Cc: Oleg Nesterov <oleg@redhat.com>
Link: https://lore.kernel.org/r/20250320234301.8342-4-chang.seok.bae@intel.com

authored by

Chang S. Bae and committed by
Ingo Molnar
a758ae28 15d51a2f

+9 -2
+9 -2
arch/x86/kernel/fpu/xstate.c
··· 581 581 static unsigned int xstate_calculate_size(u64 xfeatures, bool compacted) 582 582 { 583 583 unsigned int topmost = fls64(xfeatures) - 1; 584 - unsigned int offset = xstate_offsets[topmost]; 584 + unsigned int offset, i; 585 585 586 586 if (topmost <= XFEATURE_SSE) 587 587 return sizeof(struct xregs_state); 588 588 589 - if (compacted) 589 + if (compacted) { 590 590 offset = xfeature_get_offset(xfeatures, topmost); 591 + } else { 592 + /* Walk through the xfeature order to pick the last */ 593 + for_each_extended_xfeature_in_order(i, xfeatures) 594 + topmost = xfeature_uncompact_order[i]; 595 + offset = xstate_offsets[topmost]; 596 + } 597 + 591 598 return offset + xstate_sizes[topmost]; 592 599 } 593 600