compel: fix heap alignment for structs with xsave state

struct parasite_ctl, parasite_thread_ctl, and plain_regs_struct
all contain user_fpregs_struct_t (typedef for struct xsave_struct),
which is declared with __aligned(64). When these structs are
allocated on the heap with xmalloc/xzalloc (i.e. malloc/calloc),
the allocator only guarantees 16-byte alignment on x86_64.

The compiler, seeing the __aligned(64) attribute on the struct
type, may emit aligned memory instructions (e.g. movaps, vmovdqa)
for struct copies, assuming the memory is properly aligned. When
the heap pointer is not 64-byte aligned, these instructions trigger
a General Protection Fault (#GP).

This was observed as a crash in save_regs_plain() at infect.c:1314
(prs->regs = *r) on CentOS Stream 10 under QEMU/KVM, where the
compiler generated aligned vector instructions for the struct copy.
The crash did not reproduce on bare metal with a different compiler
version that happened to emit unaligned instructions.

Add xmemalign() wrapper around posix_memalign() to xmalloc.h and
use it for all three allocation sites:
 - compel_prepare_noctx(): struct parasite_ctl
 - compel_prepare_thread(): struct parasite_thread_ctl
 - compel_prepare(): struct plain_regs_struct

Memory from posix_memalign() can be freed with free(), so no
changes to cleanup paths are needed.

Assisted-by: Claude:claude-opus-4-6
Signed-off-by: Adrian Reber <areber@redhat.com>
This commit is contained in:
Adrian Reber 2026-03-16 15:21:05 +00:00 committed by Radostin Stoyanov
parent e39ff52283
commit 4bcf08ce7f
2 changed files with 15 additions and 3 deletions

View file

@ -1115,7 +1115,7 @@ struct parasite_thread_ctl *compel_prepare_thread(struct parasite_ctl *ctl, int
{
struct parasite_thread_ctl *tctl;
tctl = xmalloc(sizeof(*tctl));
tctl = xmemalign(__alignof__(*tctl), sizeof(*tctl));
if (tctl) {
if (prepare_thread(pid, &tctl->th)) {
xfree(tctl);
@ -1160,11 +1160,12 @@ struct parasite_ctl *compel_prepare_noctx(int pid)
/*
* Control block early setup.
*/
ctl = xzalloc(sizeof(*ctl));
ctl = xmemalign(__alignof__(*ctl), sizeof(*ctl));
if (!ctl) {
pr_err("Parasite control block allocation failed (pid: %d)\n", pid);
goto err;
}
memset(ctl, 0, sizeof(*ctl));
ctl->tsock = -1;
ctl->ictx.log_fd = -1;
@ -1363,7 +1364,8 @@ struct parasite_ctl *compel_prepare(int pid)
ictx->save_regs = save_regs_plain;
ictx->make_sigframe = make_sigframe_plain;
ictx->regs_arg = xmalloc(sizeof(struct plain_regs_struct));
ictx->regs_arg = xmemalign(__alignof__(struct plain_regs_struct),
sizeof(struct plain_regs_struct));
if (ictx->regs_arg == NULL)
goto err;

View file

@ -21,6 +21,16 @@
#define xzalloc(size) __xalloc(calloc, size, 1, size)
#define xrealloc(p, size) __xalloc(realloc, size, p, size)
#define xmemalign(align, size) \
({ \
void *___p = NULL; \
int ___err = posix_memalign(&___p, align, size); \
if (___err) \
pr_err("%s: Can't allocate %li bytes aligned to %li\n", \
__func__, (long)(size), (long)(align)); \
___p; \
})
#define xfree(p) free(p)
#define xrealloc_safe(pptr, size) \