In the function alloc_domaininfo() the arch_config field in domaininfo
is only filled inside #if defined(__i386__) || defined(__x86_64__). On
ARM the field is left unpopulated. caml_alloc_tuple() does not zero new
blocks, so di.Xenctrl.arch_config on ARM is uninitialised garbage for
every caller of Xenctrl.domain_getinfolist/domain_getinfo. Any toolstack
dereferencing that value, segfaults once a VM is enumerated in a domain
scan.
Add the missing ARM equivalent, populating xen_arm_arch_domainconfig
from the raw xc_domaininfo_t the same way the x86 branch does for
xen_x86_arch_domainconfig.
Signed-off-by: Julian Vetter <julian.vetter@vates.tech>
---
tools/ocaml/libs/xc/xenctrl_stubs.c | 14 ++++++++++++++
1 file changed, 14 insertions(+)
diff --git a/tools/ocaml/libs/xc/xenctrl_stubs.c b/tools/ocaml/libs/xc/xenctrl_stubs.c
index 7f6381cdd2..b60ea9f12e 100644
--- a/tools/ocaml/libs/xc/xenctrl_stubs.c
+++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
@@ -415,6 +415,7 @@ static value alloc_domaininfo(xc_domaininfo_t * info)
{
CAMLparam0();
CAMLlocal5(result, tmp, arch_config, x86_arch_config, emul_list);
+ CAMLlocal1(arm_arch_config);
int i;
result = caml_alloc_tuple(17);
@@ -460,6 +461,19 @@ static value alloc_domaininfo(xc_domaininfo_t * info)
Store_field(arch_config, 0, x86_arch_config);
+ Store_field(result, 16, arch_config);
+#elif defined(__arm__) || defined(__aarch64__)
+ /* xen_arm_arch_domainconfig */
+ arm_arch_config = caml_alloc_tuple(3);
+ Store_field(arm_arch_config, 0, Val_int(info->arch_config.gic_version));
+ Store_field(arm_arch_config, 1, Val_int(info->arch_config.nr_spis));
+ Store_field(arm_arch_config, 2, caml_copy_int32(info->arch_config.clock_frequency));
+
+ /* arch_config: arch_domainconfig */
+ arch_config = caml_alloc_small(1, 0);
+
+ Store_field(arch_config, 0, arm_arch_config);
+
Store_field(result, 16, arch_config);
#endif
--
2.53.0
--
Julian Vetter | Vates Hypervisor & Kernel Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
On 27/07/2026 10:11 am, Julian Vetter wrote: > In the function alloc_domaininfo() the arch_config field in domaininfo > is only filled inside #if defined(__i386__) || defined(__x86_64__). On > ARM the field is left unpopulated. caml_alloc_tuple() does not zero new > blocks, Hmm. Yes it does. caml_alloc_tuple() calls caml_alloc() which initialises with Val_unit everywhere. It is only caml_alloc_small() which doesn't allocate, and potentially ends up with garbage. Do you have details of the crash? I think it ought to be a plain NULL(ish) deference from trying to interpret Val_unit as a pointer. ~Andrew
On 7/28/26 14:59, Andrew Cooper wrote:
> On 27/07/2026 10:11 am, Julian Vetter wrote:
>> In the function alloc_domaininfo() the arch_config field in domaininfo
>> is only filled inside #if defined(__i386__) || defined(__x86_64__). On
>> ARM the field is left unpopulated. caml_alloc_tuple() does not zero new
>> blocks,
>
> Hmm. Yes it does. caml_alloc_tuple() calls caml_alloc() which
> initialises with Val_unit everywhere.
>
> It is only caml_alloc_small() which doesn't allocate, and potentially
> ends up with garbage.
>
> Do you have details of the crash? I think it ought to be a plain
> NULL(ish) deference from trying to interpret Val_unit as a pointer.
>
Sorry, you're right. My phrasing is actually wrong. Because as you said,
it's not garbage. Because it is zero'ed. It's just a "almost" NULL
pointer dereference and it terminates with a SIGSEGV.
I created a backtrace yesterday. Unfortunately I replaced the binaries
already and modified a lot of stuff in between for debugging. But here
are the details from my notes:
$ coredumpctl debug -1
...
Core was generated by `/usr/sbin/xenopsd-xc --config /etc/xenopsd.conf
--config-dir /etc/xenopsd.conf.'.
Program terminated with signal SIGSEGV, Segmentation fault.
#0 0x0000aaaadd311728 in camlXenops_server_xen__fun_14373 ()
[Current thread is 1 (LWP 21348)]
(gdb) bt
#0 camlXenops_server_xen__fun_14373 ()
at ocaml/xenopsd/xc/xenops_server_xen.ml:3368
#1 0x0000aaaadd390d34 in camlXenops_server__fun_17304 ()
at ocaml/xenopsd/lib/xenops_server.ml:3229
#2 0x0000aaaadd372c64 in camlXenops_task__with_tracing_1305 ()
at ocaml/xenopsd/lib/xenops_task.ml:103
#3 0x0000aaaadd394100 in camlXenops_server__one_7593 ()
at ocaml/xenopsd/lib/xenops_server.ml:3401
#4 0x0000aaaadd3943e8 in camlXenops_server__fun_19063 ()
at ocaml/xenopsd/lib/xenops_server.ml:3432
#5 0x0000aaaadd3be558 in camlTask_server__run_1022 ()
at ocaml/xapi-idl/lib/task_server.ml:192
#6 0x0000aaaadd45ab2c in camlDebug__fun_2365 () at
ocaml/libs/log/debug.ml:258
#7 0x0000aaaadd556dec in camlBacktrace__with_backtraces_1095 ()
#8 0x0000aaaadd45aa0c in camlDebug__with_thread_associated_inner_2357 ()
at ocaml/libs/log/debug.ml:257
#9 0x0000aaaadd38359c in camlXenops_server__fun_13600 ()
at ocaml/xenopsd/lib/xenops_server.ml:1174
#10 0x0000aaaadd5b4b84 in camlThread__fun_850 ()
#11 0x0000aaaadd6700c8 in caml_start_program ()
#12 0x0000aaaadd664a2c in caml_callback_exn ()
#13 0x0000aaaadd63ee88 in caml_thread_start ()
#14 0x0000ffffb0ecba88 in start_thread () from /lib64/libc.so.6
#15 0x0000ffffb0f36e8c in thread_start () from /lib64/libc.so.6
Line 3368 is match di.Xenctrl.arch_config with ARM _ -> true | X86 _ ->
false
ldr x0, [sp, #192] ; x0 = di # -> the domaininfo tuple pointer
ldr x23, [x0, #128] ; x23 = Field(di, 16) # -> word 16 = arch_config
and then it faults on the very next instruction when it tries to
determine whether it's ARM or x86.
> ~Andrew
>
--
Julian Vetter | Vates Hypervisor & Kernel Developer
XCP-ng & Xen Orchestra - Vates solutions
web: https://vates.tech
On 27/07/2026 10:11 am, Julian Vetter wrote:
> In the function alloc_domaininfo() the arch_config field in domaininfo
> is only filled inside #if defined(__i386__) || defined(__x86_64__). On
> ARM the field is left unpopulated. caml_alloc_tuple() does not zero new
> blocks, so di.Xenctrl.arch_config on ARM is uninitialised garbage for
> every caller of Xenctrl.domain_getinfolist/domain_getinfo. Any toolstack
> dereferencing that value, segfaults once a VM is enumerated in a domain
> scan.
>
> Add the missing ARM equivalent, populating xen_arm_arch_domainconfig
> from the raw xc_domaininfo_t the same way the x86 branch does for
> xen_x86_arch_domainconfig.
>
> Signed-off-by: Julian Vetter <julian.vetter@vates.tech>
> ---
> tools/ocaml/libs/xc/xenctrl_stubs.c | 14 ++++++++++++++
> 1 file changed, 14 insertions(+)
>
> diff --git a/tools/ocaml/libs/xc/xenctrl_stubs.c b/tools/ocaml/libs/xc/xenctrl_stubs.c
> index 7f6381cdd2..b60ea9f12e 100644
> --- a/tools/ocaml/libs/xc/xenctrl_stubs.c
> +++ b/tools/ocaml/libs/xc/xenctrl_stubs.c
> @@ -415,6 +415,7 @@ static value alloc_domaininfo(xc_domaininfo_t * info)
> {
> CAMLparam0();
> CAMLlocal5(result, tmp, arch_config, x86_arch_config, emul_list);
> + CAMLlocal1(arm_arch_config);
> int i;
>
> result = caml_alloc_tuple(17);
> @@ -460,6 +461,19 @@ static value alloc_domaininfo(xc_domaininfo_t * info)
>
> Store_field(arch_config, 0, x86_arch_config);
>
> + Store_field(result, 16, arch_config);
> +#elif defined(__arm__) || defined(__aarch64__)
> + /* xen_arm_arch_domainconfig */
> + arm_arch_config = caml_alloc_tuple(3);
> + Store_field(arm_arch_config, 0, Val_int(info->arch_config.gic_version));
> + Store_field(arm_arch_config, 1, Val_int(info->arch_config.nr_spis));
> + Store_field(arm_arch_config, 2, caml_copy_int32(info->arch_config.clock_frequency));
> +
> + /* arch_config: arch_domainconfig */
> + arch_config = caml_alloc_small(1, 0);
> +
> + Store_field(arch_config, 0, arm_arch_config);
> +
> Store_field(result, 16, arch_config);
> #endif
>
Oops, this was unintentional, but obvious now you point it out.
While this patch works, it's continuing a bad pattern I accidentally
started.
I'll do a prep cleanup patch, and rebase this one on top.
~Andrew
© 2016 - 2026 Red Hat, Inc.