criu/include/common/xmalloc.h
Adrian Reber 4bcf08ce7f 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>
2026-03-25 04:31:18 +01:00

78 lines
2.6 KiB
C

#ifndef __COMMON_XMALLOC_H__
#define __COMMON_XMALLOC_H__
#include <stdlib.h>
#include <string.h>
#ifndef pr_err
#error "Macro pr_err is needed."
#endif
#define __xalloc(op, size, ...) \
({ \
void *___p = op(__VA_ARGS__); \
if (!___p) \
pr_err("%s: Can't allocate %li bytes\n", __func__, (long)(size)); \
___p; \
})
#define xstrdup(str) __xalloc(strdup, strlen(str) + 1, str)
#define xmalloc(size) __xalloc(malloc, size, size)
#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) \
({ \
int __ret = -1; \
void *new = xrealloc(*pptr, size); \
if (new) { \
*pptr = new; \
__ret = 0; \
} \
__ret; \
})
#define xmemdup(ptr, size) \
({ \
void *new = xmalloc(size); \
if (new) \
memcpy(new, ptr, size); \
new; \
})
#define memzero_p(p) memset(p, 0, sizeof(*p))
#define memzero(p, size) memset(p, 0, size)
/*
* Helper for allocating trees with single xmalloc.
* This one advances the void *pointer on s bytes and
* returns the previous value. Use like this
*
* m = xmalloc(total_size);
* a = xptr_pull(&m, tree_root_t);
* a->b = xptr_pull(&m, leaf_a_t);
* a->c = xptr_pull(&m, leaf_c_t);
* ...
*/
static inline void *xptr_pull_s(void **m, size_t s)
{
void *ret = (*m);
(*m) += s;
return ret;
}
#define xptr_pull(m, type) xptr_pull_s(m, sizeof(type))
#endif /* __CR_XMALLOC_H__ */