* [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails
@ 2026-08-18 6:32 Furkan Caliskan
2026-08-18 6:32 ` [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() Furkan Caliskan
` (2 more replies)
0 siblings, 3 replies; 14+ messages in thread
From: Furkan Caliskan @ 2026-08-18 6:32 UTC (permalink / raw)
To: xen-devel
Cc: jgross, jbeulich, andrew.cooper3, dfaggioli, gwd, Furkan Caliskan
Furkan Caliskan (2):
xen/sched: core: skip missing vcpu slots in sched_move_domain()
xen/sched: core: kill unarmed timers on sched_init_vcpu() failure
xen/common/sched/core.c | 22 ++++++++++++++++++++++
1 file changed, 22 insertions(+)
--
2.34.1
^ permalink raw reply [flat|nested] 14+ messages in thread* [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 6:32 [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Furkan Caliskan @ 2026-08-18 6:32 ` Furkan Caliskan 2026-08-18 7:11 ` Jürgen Groß 2026-08-18 6:32 ` [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure Furkan Caliskan 2026-08-18 6:48 ` [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Jan Beulich 2 siblings, 1 reply; 14+ messages in thread From: Furkan Caliskan @ 2026-08-18 6:32 UTC (permalink / raw) To: xen-devel Cc: jgross, jbeulich, andrew.cooper3, dfaggioli, gwd, Furkan Caliskan sched_move_domain() derives the number of units to rebuild from d->max_vcpus, which is fixed at domain creation and never rolled back if vcpu_create() fails partway through building a domain. So d->vcpu[i] can be NULL for some i even though max_vcpus still counts it - this happens if sched_alloc_udata() returns NULL. The per-unit loop doesn't check for this: it sets unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken unit straight to the destination scheduler's alloc_udata(), which assumes vcpu_list is always valid and crashes Xen when it is not. Reproduced by building a domain in a non-default cpupool where vcpu creation fails partway through, then destroying it. domain_kill() moves the domain back to the default cpupool via sched_move_domain() before actually destroying it, crashing inside the destination scheduler's alloc_udata() (seen in Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). Before building a unit in sched_move_domain(), check that all of its vcpu slots are populated, and skip it if any are missing. The rest of the function walks the vcpus that actually exist, via for_each_vcpu() rather than n_units, so skipping a unit here does not leave anything else out of sync. Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> --- xen/common/sched/core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c index d3a0a97e1d..d542c76543 100644 --- a/xen/common/sched/core.c +++ b/xen/common/sched/core.c @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, struct cpupool *c) for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) { + /* + * Skip this unit if any of its vcpus is missing. Bounded by + * max_vcpus. + */ + bool vcpu_failed = false; + + for ( unsigned int i = 0; + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) + { + if ( !d->vcpu[unit_idx * gran + i] ) + { + vcpu_failed = true; + break; + } + } + + if ( vcpu_failed ) + continue; + unit = sched_alloc_unit_mem(); if ( unit ) { -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 6:32 ` [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() Furkan Caliskan @ 2026-08-18 7:11 ` Jürgen Groß 2026-08-18 7:53 ` Furkan Çalışkan 0 siblings, 1 reply; 14+ messages in thread From: Jürgen Groß @ 2026-08-18 7:11 UTC (permalink / raw) To: Furkan Caliskan, xen-devel; +Cc: jbeulich, andrew.cooper3, dfaggioli, gwd [-- Attachment #1.1.1: Type: text/plain, Size: 2824 bytes --] On 18.08.26 08:32, Furkan Caliskan wrote: > sched_move_domain() derives the number of units to rebuild from > d->max_vcpus, which is fixed at domain creation and never rolled > back if vcpu_create() fails partway through building a domain. So > d->vcpu[i] can be NULL for some i even though max_vcpus still > counts it - this happens if sched_alloc_udata() returns NULL. > > The per-unit loop doesn't check for this: it sets > unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken > unit straight to the destination scheduler's alloc_udata(), > which assumes vcpu_list is always valid and crashes Xen when > it is not. > > Reproduced by building a domain in a non-default cpupool where > vcpu creation fails partway through, then destroying it. > domain_kill() moves the domain back to the default cpupool via > sched_move_domain() before actually destroying it, crashing > inside the destination scheduler's alloc_udata() (seen in > Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). > > Before building a unit in sched_move_domain(), check that all of > its vcpu slots are populated, and skip it if any are missing. The > rest of the function walks the vcpus that actually exist, via > for_each_vcpu() rather than n_units, so skipping a unit here > does not leave anything else out of sync. > > Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> > --- > xen/common/sched/core.c | 19 +++++++++++++++++++ > 1 file changed, 19 insertions(+) > > diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c > index d3a0a97e1d..d542c76543 100644 > --- a/xen/common/sched/core.c > +++ b/xen/common/sched/core.c > @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, struct cpupool *c) > > for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) > { > + /* > + * Skip this unit if any of its vcpus is missing. Bounded by > + * max_vcpus. > + */ > + bool vcpu_failed = false; > + > + for ( unsigned int i = 0; > + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) > + { > + if ( !d->vcpu[unit_idx * gran + i] ) > + { > + vcpu_failed = true; > + break; > + } > + } > + > + if ( vcpu_failed ) > + continue; I don't think this is correct. If there are some vcpus in the unit you will loose them (i.e. make them no longer be able to be scheduled), right? For a dying domain this might be okay, but not for one still active. So I think you should at least verify the domain is dying, otherwise sched_move_domain() should just fail. An alternative might be to fix the NULL dereferencing where needed, but this could become tedious. Juergen [-- Attachment #1.1.2: OpenPGP public key --] [-- Type: application/pgp-keys, Size: 3743 bytes --] [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 495 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 7:11 ` Jürgen Groß @ 2026-08-18 7:53 ` Furkan Çalışkan 2026-08-18 10:04 ` Andrew Cooper 0 siblings, 1 reply; 14+ messages in thread From: Furkan Çalışkan @ 2026-08-18 7:53 UTC (permalink / raw) To: Jürgen Groß, xen-devel; +Cc: jbeulich, andrew.cooper3, dfaggioli, gwd On 8/18/26 10:11, Jürgen Groß wrote: > On 18.08.26 08:32, Furkan Caliskan wrote: >> sched_move_domain() derives the number of units to rebuild from >> d->max_vcpus, which is fixed at domain creation and never rolled >> back if vcpu_create() fails partway through building a domain. So >> d->vcpu[i] can be NULL for some i even though max_vcpus still >> counts it - this happens if sched_alloc_udata() returns NULL. >> >> The per-unit loop doesn't check for this: it sets >> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >> unit straight to the destination scheduler's alloc_udata(), >> which assumes vcpu_list is always valid and crashes Xen when >> it is not. >> >> Reproduced by building a domain in a non-default cpupool where >> vcpu creation fails partway through, then destroying it. >> domain_kill() moves the domain back to the default cpupool via >> sched_move_domain() before actually destroying it, crashing >> inside the destination scheduler's alloc_udata() (seen in >> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >> >> Before building a unit in sched_move_domain(), check that all of >> its vcpu slots are populated, and skip it if any are missing. The >> rest of the function walks the vcpus that actually exist, via >> for_each_vcpu() rather than n_units, so skipping a unit here >> does not leave anything else out of sync. >> >> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >> --- >> xen/common/sched/core.c | 19 +++++++++++++++++++ >> 1 file changed, 19 insertions(+) >> >> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >> index d3a0a97e1d..d542c76543 100644 >> --- a/xen/common/sched/core.c >> +++ b/xen/common/sched/core.c >> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, struct cpupool *c) >> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >> { >> + /* >> + * Skip this unit if any of its vcpus is missing. Bounded by >> + * max_vcpus. >> + */ >> + bool vcpu_failed = false; >> + >> + for ( unsigned int i = 0; >> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >> + { >> + if ( !d->vcpu[unit_idx * gran + i] ) >> + { >> + vcpu_failed = true; >> + break; >> + } >> + } >> + >> + if ( vcpu_failed ) >> + continue; > > I don't think this is correct. > > If there are some vcpus in the unit you will loose them (i.e. make them no > longer be able to be scheduled), right? > > For a dying domain this might be okay, but not for one still active. So I think > you should at least verify the domain is dying, otherwise sched_move_domain() > should just fail. > > An alternative might be to fix the NULL dereferencing where needed, but this > could become tedious. > > > Juergen Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' for the head vCPU. The crash happens when unit->vcpu_list is set to d->vcpu[unit_idx*gran] (which is NULL) and passed to 'alloc_udata()', causing a NULL dereference. I expanded the loop over 'gran' to handle core-scheduling cases where a subsequent vCPU fails mid-unit, but as you pointed out, that drops the whole unit for active domain. I'll update the patch to check d->is_dying to skip incomplete units only for dying domains, and have sched_move_domain() fail if an active domain has missing vCPUs Furkan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 7:53 ` Furkan Çalışkan @ 2026-08-18 10:04 ` Andrew Cooper 2026-08-18 10:13 ` Jürgen Groß 0 siblings, 1 reply; 14+ messages in thread From: Andrew Cooper @ 2026-08-18 10:04 UTC (permalink / raw) To: Furkan Çalışkan, Jürgen Groß, xen-devel Cc: Andrew Cooper, jbeulich, dfaggioli, gwd On 18/08/2026 8:53 am, Furkan Çalışkan wrote: > On 8/18/26 10:11, Jürgen Groß wrote: >> On 18.08.26 08:32, Furkan Caliskan wrote: >>> sched_move_domain() derives the number of units to rebuild from >>> d->max_vcpus, which is fixed at domain creation and never rolled >>> back if vcpu_create() fails partway through building a domain. So >>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>> counts it - this happens if sched_alloc_udata() returns NULL. >>> >>> The per-unit loop doesn't check for this: it sets >>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>> unit straight to the destination scheduler's alloc_udata(), >>> which assumes vcpu_list is always valid and crashes Xen when >>> it is not. >>> >>> Reproduced by building a domain in a non-default cpupool where >>> vcpu creation fails partway through, then destroying it. >>> domain_kill() moves the domain back to the default cpupool via >>> sched_move_domain() before actually destroying it, crashing >>> inside the destination scheduler's alloc_udata() (seen in >>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>> >>> Before building a unit in sched_move_domain(), check that all of >>> its vcpu slots are populated, and skip it if any are missing. The >>> rest of the function walks the vcpus that actually exist, via >>> for_each_vcpu() rather than n_units, so skipping a unit here >>> does not leave anything else out of sync. >>> >>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>> --- >>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>> 1 file changed, 19 insertions(+) >>> >>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>> index d3a0a97e1d..d542c76543 100644 >>> --- a/xen/common/sched/core.c >>> +++ b/xen/common/sched/core.c >>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, struct cpupool *c) >>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>> { >>> + /* >>> + * Skip this unit if any of its vcpus is missing. Bounded by >>> + * max_vcpus. >>> + */ >>> + bool vcpu_failed = false; >>> + >>> + for ( unsigned int i = 0; >>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>> + { >>> + if ( !d->vcpu[unit_idx * gran + i] ) >>> + { >>> + vcpu_failed = true; >>> + break; >>> + } >>> + } >>> + >>> + if ( vcpu_failed ) >>> + continue; >> I don't think this is correct. >> >> If there are some vcpus in the unit you will loose them (i.e. make them no >> longer be able to be scheduled), right? >> >> For a dying domain this might be okay, but not for one still active. So I think >> you should at least verify the domain is dying, otherwise sched_move_domain() >> should just fail. >> >> An alternative might be to fix the NULL dereferencing where needed, but this >> could become tedious. >> >> >> Juergen > Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' > for the head vCPU. The crash happens when unit->vcpu_list is set > to d->vcpu[unit_idx*gran] (which is NULL) and passed to 'alloc_udata()', > causing a NULL dereference. > > I expanded the loop over 'gran' to handle core-scheduling cases where a > subsequent vCPU fails mid-unit, but as you pointed out, that drops the > whole unit for active domain. > > I'll update the patch to check d->is_dying to skip incomplete units > only for dying domains, and have sched_move_domain() fail if an active > domain has missing vCPUs I'm afraid that wont fix everything. Your scenario is rare, but sadly we have no interlock to kill the domain if a setmaxvcpus hypercall finishes mid-way through. Furthermore, for dom0 at least, we actively do want to run in this configuration if we end up in it, because that at least helps recovery of the system. At all points in a domain's lifecycle, d->vcpu[0..max_vcpus] may be NULL, and we may even want to schedule in this scenario. ~Andrew ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 10:04 ` Andrew Cooper @ 2026-08-18 10:13 ` Jürgen Groß 2026-08-18 10:35 ` Andrew Cooper 0 siblings, 1 reply; 14+ messages in thread From: Jürgen Groß @ 2026-08-18 10:13 UTC (permalink / raw) To: Andrew Cooper, Furkan Çalışkan, xen-devel Cc: jbeulich, dfaggioli, gwd [-- Attachment #1.1.1: Type: text/plain, Size: 4504 bytes --] On 18.08.26 12:04, Andrew Cooper wrote: > On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >> On 8/18/26 10:11, Jürgen Groß wrote: >>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>> sched_move_domain() derives the number of units to rebuild from >>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>> back if vcpu_create() fails partway through building a domain. So >>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>> >>>> The per-unit loop doesn't check for this: it sets >>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>> unit straight to the destination scheduler's alloc_udata(), >>>> which assumes vcpu_list is always valid and crashes Xen when >>>> it is not. >>>> >>>> Reproduced by building a domain in a non-default cpupool where >>>> vcpu creation fails partway through, then destroying it. >>>> domain_kill() moves the domain back to the default cpupool via >>>> sched_move_domain() before actually destroying it, crashing >>>> inside the destination scheduler's alloc_udata() (seen in >>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>> >>>> Before building a unit in sched_move_domain(), check that all of >>>> its vcpu slots are populated, and skip it if any are missing. The >>>> rest of the function walks the vcpus that actually exist, via >>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>> does not leave anything else out of sync. >>>> >>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>> --- >>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>> 1 file changed, 19 insertions(+) >>>> >>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>> index d3a0a97e1d..d542c76543 100644 >>>> --- a/xen/common/sched/core.c >>>> +++ b/xen/common/sched/core.c >>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, struct cpupool *c) >>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>> { >>>> + /* >>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>> + * max_vcpus. >>>> + */ >>>> + bool vcpu_failed = false; >>>> + >>>> + for ( unsigned int i = 0; >>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>> + { >>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>> + { >>>> + vcpu_failed = true; >>>> + break; >>>> + } >>>> + } >>>> + >>>> + if ( vcpu_failed ) >>>> + continue; >>> I don't think this is correct. >>> >>> If there are some vcpus in the unit you will loose them (i.e. make them no >>> longer be able to be scheduled), right? >>> >>> For a dying domain this might be okay, but not for one still active. So I think >>> you should at least verify the domain is dying, otherwise sched_move_domain() >>> should just fail. >>> >>> An alternative might be to fix the NULL dereferencing where needed, but this >>> could become tedious. >>> >>> >>> Juergen >> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >> for the head vCPU. The crash happens when unit->vcpu_list is set >> to d->vcpu[unit_idx*gran] (which is NULL) and passed to 'alloc_udata()', >> causing a NULL dereference. >> >> I expanded the loop over 'gran' to handle core-scheduling cases where a >> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >> whole unit for active domain. >> >> I'll update the patch to check d->is_dying to skip incomplete units >> only for dying domains, and have sched_move_domain() fail if an active >> domain has missing vCPUs > > I'm afraid that wont fix everything. Why not? > Your scenario is rare, but sadly we have no interlock to kill the domain > if a setmaxvcpus hypercall finishes mid-way through. Furthermore, for > dom0 at least, we actively do want to run in this configuration if we > end up in it, because that at least helps recovery of the system. It is only about moving a domain to another cpupool. Dom0 can't be moved anyway, and not being able to move a crippled domain is better than a crash. So what is the problem then? Juergen [-- Attachment #1.1.2: OpenPGP public key --] [-- Type: application/pgp-keys, Size: 3743 bytes --] [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 495 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 10:13 ` Jürgen Groß @ 2026-08-18 10:35 ` Andrew Cooper 2026-08-18 10:47 ` Juergen Gross 2026-08-18 10:53 ` Jan Beulich 0 siblings, 2 replies; 14+ messages in thread From: Andrew Cooper @ 2026-08-18 10:35 UTC (permalink / raw) To: Jürgen Groß, Furkan Çalışkan, xen-devel Cc: Andrew Cooper, jbeulich, dfaggioli, gwd On 18/08/2026 11:13 am, Jürgen Groß wrote: > On 18.08.26 12:04, Andrew Cooper wrote: >> On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >>> On 8/18/26 10:11, Jürgen Groß wrote: >>>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>>> sched_move_domain() derives the number of units to rebuild from >>>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>>> back if vcpu_create() fails partway through building a domain. So >>>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>>> >>>>> The per-unit loop doesn't check for this: it sets >>>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>>> unit straight to the destination scheduler's alloc_udata(), >>>>> which assumes vcpu_list is always valid and crashes Xen when >>>>> it is not. >>>>> >>>>> Reproduced by building a domain in a non-default cpupool where >>>>> vcpu creation fails partway through, then destroying it. >>>>> domain_kill() moves the domain back to the default cpupool via >>>>> sched_move_domain() before actually destroying it, crashing >>>>> inside the destination scheduler's alloc_udata() (seen in >>>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>>> >>>>> Before building a unit in sched_move_domain(), check that all of >>>>> its vcpu slots are populated, and skip it if any are missing. The >>>>> rest of the function walks the vcpus that actually exist, via >>>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>>> does not leave anything else out of sync. >>>>> >>>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>>> --- >>>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>>> 1 file changed, 19 insertions(+) >>>>> >>>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>>> index d3a0a97e1d..d542c76543 100644 >>>>> --- a/xen/common/sched/core.c >>>>> +++ b/xen/common/sched/core.c >>>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, >>>>> struct cpupool *c) >>>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>>> { >>>>> + /* >>>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>>> + * max_vcpus. >>>>> + */ >>>>> + bool vcpu_failed = false; >>>>> + >>>>> + for ( unsigned int i = 0; >>>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>>> + { >>>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>>> + { >>>>> + vcpu_failed = true; >>>>> + break; >>>>> + } >>>>> + } >>>>> + >>>>> + if ( vcpu_failed ) >>>>> + continue; >>>> I don't think this is correct. >>>> >>>> If there are some vcpus in the unit you will loose them (i.e. make >>>> them no >>>> longer be able to be scheduled), right? >>>> >>>> For a dying domain this might be okay, but not for one still >>>> active. So I think >>>> you should at least verify the domain is dying, otherwise >>>> sched_move_domain() >>>> should just fail. >>>> >>>> An alternative might be to fix the NULL dereferencing where needed, >>>> but this >>>> could become tedious. >>>> >>>> >>>> Juergen >>> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >>> for the head vCPU. The crash happens when unit->vcpu_list is set >>> to d->vcpu[unit_idx*gran] (which is NULL) and passed to >>> 'alloc_udata()', >>> causing a NULL dereference. >>> >>> I expanded the loop over 'gran' to handle core-scheduling cases where a >>> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >>> whole unit for active domain. >>> >>> I'll update the patch to check d->is_dying to skip incomplete units >>> only for dying domains, and have sched_move_domain() fail if an active >>> domain has missing vCPUs >> >> I'm afraid that wont fix everything. > > Why not? domU's in this situation do not have is_dying set. ~Andrew ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 10:35 ` Andrew Cooper @ 2026-08-18 10:47 ` Juergen Gross 2026-08-18 10:53 ` Jan Beulich 1 sibling, 0 replies; 14+ messages in thread From: Juergen Gross @ 2026-08-18 10:47 UTC (permalink / raw) To: Andrew Cooper, Furkan Çalışkan, xen-devel Cc: jbeulich, dfaggioli, gwd [-- Attachment #1.1.1: Type: text/plain, Size: 4833 bytes --] On 18.08.26 12:35, Andrew Cooper wrote: > On 18/08/2026 11:13 am, Jürgen Groß wrote: >> On 18.08.26 12:04, Andrew Cooper wrote: >>> On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >>>> On 8/18/26 10:11, Jürgen Groß wrote: >>>>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>>>> sched_move_domain() derives the number of units to rebuild from >>>>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>>>> back if vcpu_create() fails partway through building a domain. So >>>>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>>>> >>>>>> The per-unit loop doesn't check for this: it sets >>>>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>>>> unit straight to the destination scheduler's alloc_udata(), >>>>>> which assumes vcpu_list is always valid and crashes Xen when >>>>>> it is not. >>>>>> >>>>>> Reproduced by building a domain in a non-default cpupool where >>>>>> vcpu creation fails partway through, then destroying it. >>>>>> domain_kill() moves the domain back to the default cpupool via >>>>>> sched_move_domain() before actually destroying it, crashing >>>>>> inside the destination scheduler's alloc_udata() (seen in >>>>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>>>> >>>>>> Before building a unit in sched_move_domain(), check that all of >>>>>> its vcpu slots are populated, and skip it if any are missing. The >>>>>> rest of the function walks the vcpus that actually exist, via >>>>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>>>> does not leave anything else out of sync. >>>>>> >>>>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>>>> --- >>>>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>>>> 1 file changed, 19 insertions(+) >>>>>> >>>>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>>>> index d3a0a97e1d..d542c76543 100644 >>>>>> --- a/xen/common/sched/core.c >>>>>> +++ b/xen/common/sched/core.c >>>>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, >>>>>> struct cpupool *c) >>>>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>>>> { >>>>>> + /* >>>>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>>>> + * max_vcpus. >>>>>> + */ >>>>>> + bool vcpu_failed = false; >>>>>> + >>>>>> + for ( unsigned int i = 0; >>>>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>>>> + { >>>>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>>>> + { >>>>>> + vcpu_failed = true; >>>>>> + break; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + if ( vcpu_failed ) >>>>>> + continue; >>>>> I don't think this is correct. >>>>> >>>>> If there are some vcpus in the unit you will loose them (i.e. make >>>>> them no >>>>> longer be able to be scheduled), right? >>>>> >>>>> For a dying domain this might be okay, but not for one still >>>>> active. So I think >>>>> you should at least verify the domain is dying, otherwise >>>>> sched_move_domain() >>>>> should just fail. >>>>> >>>>> An alternative might be to fix the NULL dereferencing where needed, >>>>> but this >>>>> could become tedious. >>>>> >>>>> >>>>> Juergen >>>> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >>>> for the head vCPU. The crash happens when unit->vcpu_list is set >>>> to d->vcpu[unit_idx*gran] (which is NULL) and passed to >>>> 'alloc_udata()', >>>> causing a NULL dereference. >>>> >>>> I expanded the loop over 'gran' to handle core-scheduling cases where a >>>> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >>>> whole unit for active domain. >>>> >>>> I'll update the patch to check d->is_dying to skip incomplete units >>>> only for dying domains, and have sched_move_domain() fail if an active >>>> domain has missing vCPUs >>> >>> I'm afraid that wont fix everything. >> >> Why not? > > domU's in this situation do not have is_dying set. Please clarify what you mean with "this situation". sched_move_domain() is being called either due to an admin action ("xl cpupool-migrate"), or during domain_kill() in order to move the domain to cpupool0 for avoiding a zombie domain blocking cpupool removal. The first case is allowed to fail, which the suggested fix would do, while the second case is happening only after DOMDYING_dying has been set for the domain. Juergen [-- Attachment #1.1.2: OpenPGP public key --] [-- Type: application/pgp-keys, Size: 3743 bytes --] [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 495 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 10:35 ` Andrew Cooper 2026-08-18 10:47 ` Juergen Gross @ 2026-08-18 10:53 ` Jan Beulich 2026-08-18 12:12 ` Furkan Çalışkan 1 sibling, 1 reply; 14+ messages in thread From: Jan Beulich @ 2026-08-18 10:53 UTC (permalink / raw) To: Andrew Cooper Cc: dfaggioli, gwd, Jürgen Groß, Furkan Çalışkan, xen-devel On 18.08.2026 12:35, Andrew Cooper wrote: > On 18/08/2026 11:13 am, Jürgen Groß wrote: >> On 18.08.26 12:04, Andrew Cooper wrote: >>> On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >>>> On 8/18/26 10:11, Jürgen Groß wrote: >>>>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>>>> sched_move_domain() derives the number of units to rebuild from >>>>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>>>> back if vcpu_create() fails partway through building a domain. So >>>>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>>>> >>>>>> The per-unit loop doesn't check for this: it sets >>>>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>>>> unit straight to the destination scheduler's alloc_udata(), >>>>>> which assumes vcpu_list is always valid and crashes Xen when >>>>>> it is not. >>>>>> >>>>>> Reproduced by building a domain in a non-default cpupool where >>>>>> vcpu creation fails partway through, then destroying it. >>>>>> domain_kill() moves the domain back to the default cpupool via >>>>>> sched_move_domain() before actually destroying it, crashing >>>>>> inside the destination scheduler's alloc_udata() (seen in >>>>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>>>> >>>>>> Before building a unit in sched_move_domain(), check that all of >>>>>> its vcpu slots are populated, and skip it if any are missing. The >>>>>> rest of the function walks the vcpus that actually exist, via >>>>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>>>> does not leave anything else out of sync. >>>>>> >>>>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>>>> --- >>>>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>>>> 1 file changed, 19 insertions(+) >>>>>> >>>>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>>>> index d3a0a97e1d..d542c76543 100644 >>>>>> --- a/xen/common/sched/core.c >>>>>> +++ b/xen/common/sched/core.c >>>>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, >>>>>> struct cpupool *c) >>>>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>>>> { >>>>>> + /* >>>>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>>>> + * max_vcpus. >>>>>> + */ >>>>>> + bool vcpu_failed = false; >>>>>> + >>>>>> + for ( unsigned int i = 0; >>>>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>>>> + { >>>>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>>>> + { >>>>>> + vcpu_failed = true; >>>>>> + break; >>>>>> + } >>>>>> + } >>>>>> + >>>>>> + if ( vcpu_failed ) >>>>>> + continue; >>>>> I don't think this is correct. >>>>> >>>>> If there are some vcpus in the unit you will loose them (i.e. make >>>>> them no >>>>> longer be able to be scheduled), right? >>>>> >>>>> For a dying domain this might be okay, but not for one still >>>>> active. So I think >>>>> you should at least verify the domain is dying, otherwise >>>>> sched_move_domain() >>>>> should just fail. >>>>> >>>>> An alternative might be to fix the NULL dereferencing where needed, >>>>> but this >>>>> could become tedious. >>>>> >>>>> >>>>> Juergen >>>> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >>>> for the head vCPU. The crash happens when unit->vcpu_list is set >>>> to d->vcpu[unit_idx*gran] (which is NULL) and passed to >>>> 'alloc_udata()', >>>> causing a NULL dereference. >>>> >>>> I expanded the loop over 'gran' to handle core-scheduling cases where a >>>> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >>>> whole unit for active domain. >>>> >>>> I'll update the patch to check d->is_dying to skip incomplete units >>>> only for dying domains, and have sched_move_domain() fail if an active >>>> domain has missing vCPUs >>> >>> I'm afraid that wont fix everything. >> >> Why not? > > domU's in this situation do not have is_dying set. Yet isn't the (separate) bug then that we allow a DomU to be launched when XEN_DOMCTL_max_vcpus didn't finish setting up all vCPU-s? Or is that what you were alluding to? Since you did say "..., and we may even want to schedule in this scenario" - perhaps not. Jan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 10:53 ` Jan Beulich @ 2026-08-18 12:12 ` Furkan Çalışkan 2026-08-18 12:19 ` Jan Beulich 0 siblings, 1 reply; 14+ messages in thread From: Furkan Çalışkan @ 2026-08-18 12:12 UTC (permalink / raw) To: Jan Beulich, Andrew Cooper Cc: dfaggioli, gwd, Jürgen Groß, xen-devel On 8/18/26 13:53, Jan Beulich wrote: > On 18.08.2026 12:35, Andrew Cooper wrote: >> On 18/08/2026 11:13 am, Jürgen Groß wrote: >>> On 18.08.26 12:04, Andrew Cooper wrote: >>>> On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >>>>> On 8/18/26 10:11, Jürgen Groß wrote: >>>>>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>>>>> sched_move_domain() derives the number of units to rebuild from >>>>>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>>>>> back if vcpu_create() fails partway through building a domain. So >>>>>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>>>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>>>>> >>>>>>> The per-unit loop doesn't check for this: it sets >>>>>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>>>>> unit straight to the destination scheduler's alloc_udata(), >>>>>>> which assumes vcpu_list is always valid and crashes Xen when >>>>>>> it is not. >>>>>>> >>>>>>> Reproduced by building a domain in a non-default cpupool where >>>>>>> vcpu creation fails partway through, then destroying it. >>>>>>> domain_kill() moves the domain back to the default cpupool via >>>>>>> sched_move_domain() before actually destroying it, crashing >>>>>>> inside the destination scheduler's alloc_udata() (seen in >>>>>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>>>>> >>>>>>> Before building a unit in sched_move_domain(), check that all of >>>>>>> its vcpu slots are populated, and skip it if any are missing. The >>>>>>> rest of the function walks the vcpus that actually exist, via >>>>>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>>>>> does not leave anything else out of sync. >>>>>>> >>>>>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>>>>> --- >>>>>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>>>>> 1 file changed, 19 insertions(+) >>>>>>> >>>>>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>>>>> index d3a0a97e1d..d542c76543 100644 >>>>>>> --- a/xen/common/sched/core.c >>>>>>> +++ b/xen/common/sched/core.c >>>>>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, >>>>>>> struct cpupool *c) >>>>>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>>>>> { >>>>>>> + /* >>>>>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>>>>> + * max_vcpus. >>>>>>> + */ >>>>>>> + bool vcpu_failed = false; >>>>>>> + >>>>>>> + for ( unsigned int i = 0; >>>>>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>>>>> + { >>>>>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>>>>> + { >>>>>>> + vcpu_failed = true; >>>>>>> + break; >>>>>>> + } >>>>>>> + } >>>>>>> + >>>>>>> + if ( vcpu_failed ) >>>>>>> + continue; >>>>>> I don't think this is correct. >>>>>> >>>>>> If there are some vcpus in the unit you will loose them (i.e. make >>>>>> them no >>>>>> longer be able to be scheduled), right? >>>>>> >>>>>> For a dying domain this might be okay, but not for one still >>>>>> active. So I think >>>>>> you should at least verify the domain is dying, otherwise >>>>>> sched_move_domain() >>>>>> should just fail. >>>>>> >>>>>> An alternative might be to fix the NULL dereferencing where needed, >>>>>> but this >>>>>> could become tedious. >>>>>> >>>>>> >>>>>> Juergen >>>>> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >>>>> for the head vCPU. The crash happens when unit->vcpu_list is set >>>>> to d->vcpu[unit_idx*gran] (which is NULL) and passed to >>>>> 'alloc_udata()', >>>>> causing a NULL dereference. >>>>> >>>>> I expanded the loop over 'gran' to handle core-scheduling cases where a >>>>> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >>>>> whole unit for active domain. >>>>> >>>>> I'll update the patch to check d->is_dying to skip incomplete units >>>>> only for dying domains, and have sched_move_domain() fail if an active >>>>> domain has missing vCPUs >>>> >>>> I'm afraid that wont fix everything. >>> >>> Why not? >> >> domU's in this situation do not have is_dying set. > > Yet isn't the (separate) bug then that we allow a DomU to be launched when > XEN_DOMCTL_max_vcpus didn't finish setting up all vCPU-s? Or is that what > you were alluding to? Since you did say "..., and we may even want to > schedule in this scenario" - perhaps not. > > Jan When vcpu_create() returns NULL, XEN_DOMCTL_max_vcpus returns an error. Once the toolstack sees that, it immediately issues the kill hypercall. AFAIK, a domain in that state will simply be killed and never actually launched. Furkan ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() 2026-08-18 12:12 ` Furkan Çalışkan @ 2026-08-18 12:19 ` Jan Beulich 0 siblings, 0 replies; 14+ messages in thread From: Jan Beulich @ 2026-08-18 12:19 UTC (permalink / raw) To: Furkan Çalışkan Cc: dfaggioli, gwd, Jürgen Groß, xen-devel, Andrew Cooper On 18.08.2026 14:12, Furkan Çalışkan wrote: > > > On 8/18/26 13:53, Jan Beulich wrote: >> On 18.08.2026 12:35, Andrew Cooper wrote: >>> On 18/08/2026 11:13 am, Jürgen Groß wrote: >>>> On 18.08.26 12:04, Andrew Cooper wrote: >>>>> On 18/08/2026 8:53 am, Furkan Çalışkan wrote: >>>>>> On 8/18/26 10:11, Jürgen Groß wrote: >>>>>>> On 18.08.26 08:32, Furkan Caliskan wrote: >>>>>>>> sched_move_domain() derives the number of units to rebuild from >>>>>>>> d->max_vcpus, which is fixed at domain creation and never rolled >>>>>>>> back if vcpu_create() fails partway through building a domain. So >>>>>>>> d->vcpu[i] can be NULL for some i even though max_vcpus still >>>>>>>> counts it - this happens if sched_alloc_udata() returns NULL. >>>>>>>> >>>>>>>> The per-unit loop doesn't check for this: it sets >>>>>>>> unit->vcpu_list = d->vcpu[unit_id] (NULL) and hands that broken >>>>>>>> unit straight to the destination scheduler's alloc_udata(), >>>>>>>> which assumes vcpu_list is always valid and crashes Xen when >>>>>>>> it is not. >>>>>>>> >>>>>>>> Reproduced by building a domain in a non-default cpupool where >>>>>>>> vcpu creation fails partway through, then destroying it. >>>>>>>> domain_kill() moves the domain back to the default cpupool via >>>>>>>> sched_move_domain() before actually destroying it, crashing >>>>>>>> inside the destination scheduler's alloc_udata() (seen in >>>>>>>> Credit2's csched2_alloc_udata() -> is_idle_unit() -> NULL deref). >>>>>>>> >>>>>>>> Before building a unit in sched_move_domain(), check that all of >>>>>>>> its vcpu slots are populated, and skip it if any are missing. The >>>>>>>> rest of the function walks the vcpus that actually exist, via >>>>>>>> for_each_vcpu() rather than n_units, so skipping a unit here >>>>>>>> does not leave anything else out of sync. >>>>>>>> >>>>>>>> Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> >>>>>>>> --- >>>>>>>> xen/common/sched/core.c | 19 +++++++++++++++++++ >>>>>>>> 1 file changed, 19 insertions(+) >>>>>>>> >>>>>>>> diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c >>>>>>>> index d3a0a97e1d..d542c76543 100644 >>>>>>>> --- a/xen/common/sched/core.c >>>>>>>> +++ b/xen/common/sched/core.c >>>>>>>> @@ -745,6 +745,25 @@ int sched_move_domain(struct domain *d, >>>>>>>> struct cpupool *c) >>>>>>>> for ( unit_idx = 0; unit_idx < n_units; unit_idx++ ) >>>>>>>> { >>>>>>>> + /* >>>>>>>> + * Skip this unit if any of its vcpus is missing. Bounded by >>>>>>>> + * max_vcpus. >>>>>>>> + */ >>>>>>>> + bool vcpu_failed = false; >>>>>>>> + >>>>>>>> + for ( unsigned int i = 0; >>>>>>>> + i < gran && unit_idx * gran + i < d->max_vcpus; i++ ) >>>>>>>> + { >>>>>>>> + if ( !d->vcpu[unit_idx * gran + i] ) >>>>>>>> + { >>>>>>>> + vcpu_failed = true; >>>>>>>> + break; >>>>>>>> + } >>>>>>>> + } >>>>>>>> + >>>>>>>> + if ( vcpu_failed ) >>>>>>>> + continue; >>>>>>> I don't think this is correct. >>>>>>> >>>>>>> If there are some vcpus in the unit you will loose them (i.e. make >>>>>>> them no >>>>>>> longer be able to be scheduled), right? >>>>>>> >>>>>>> For a dying domain this might be okay, but not for one still >>>>>>> active. So I think >>>>>>> you should at least verify the domain is dying, otherwise >>>>>>> sched_move_domain() >>>>>>> should just fail. >>>>>>> >>>>>>> An alternative might be to fix the NULL dereferencing where needed, >>>>>>> but this >>>>>>> could become tedious. >>>>>>> >>>>>>> >>>>>>> Juergen >>>>>> Right. My initial attempt only checked 'd->vcpu[unit_idx*gran]' >>>>>> for the head vCPU. The crash happens when unit->vcpu_list is set >>>>>> to d->vcpu[unit_idx*gran] (which is NULL) and passed to >>>>>> 'alloc_udata()', >>>>>> causing a NULL dereference. >>>>>> >>>>>> I expanded the loop over 'gran' to handle core-scheduling cases where a >>>>>> subsequent vCPU fails mid-unit, but as you pointed out, that drops the >>>>>> whole unit for active domain. >>>>>> >>>>>> I'll update the patch to check d->is_dying to skip incomplete units >>>>>> only for dying domains, and have sched_move_domain() fail if an active >>>>>> domain has missing vCPUs >>>>> >>>>> I'm afraid that wont fix everything. >>>> >>>> Why not? >>> >>> domU's in this situation do not have is_dying set. >> >> Yet isn't the (separate) bug then that we allow a DomU to be launched when >> XEN_DOMCTL_max_vcpus didn't finish setting up all vCPU-s? Or is that what >> you were alluding to? Since you did say "..., and we may even want to >> schedule in this scenario" - perhaps not. > > When vcpu_create() returns NULL, XEN_DOMCTL_max_vcpus returns an > error. Once the toolstack sees that, it immediately issues the > kill hypercall. That's what the one toolstack you look at does. At the hypervisor level, what a toolstack may do after a failure is entirely unknown. Jan ^ permalink raw reply [flat|nested] 14+ messages in thread
* [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure 2026-08-18 6:32 [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Furkan Caliskan 2026-08-18 6:32 ` [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() Furkan Caliskan @ 2026-08-18 6:32 ` Furkan Caliskan 2026-08-18 7:23 ` Jürgen Groß 2026-08-18 6:48 ` [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Jan Beulich 2 siblings, 1 reply; 14+ messages in thread From: Furkan Caliskan @ 2026-08-18 6:32 UTC (permalink / raw) To: xen-devel Cc: jgross, jbeulich, andrew.cooper3, dfaggioli, gwd, Furkan Caliskan sched_init_vcpu() calls init_timer() for a vcpu's periodic_timer, singleshot_timer and poll_timer before it can fail -- these become live, linked into their target pCPU's per-cpu timer list regardless of what happens next. If the sched_alloc_udata() call further down then fails, the function frees the sched_unit via sched_free_unit() and returns 1, but never unlinks these three timers. The caller, vcpu_create(), does worse: on sched_init_vcpu() returning nonzero it jumps to fail_wq, skipping fail_sched and thus sched_destroy_vcpu() -- the only function on this path that calls kill_timer() on them. vcpu_destroy() then frees the vcpu, and the three timers embedded in it, while they are still linked into that shared list. This silently corrupts that list. It only shows up later, when something else touches a neighboring timer: sched_move_domain() crashed with "Assertion 'entry->prev->next == entry' failed" on a completely unrelated, valid vcpus's timer. Kill all three timers in sched_init_vcpu()'s own failure branch, so it doesn't depend on the caller reaching sched_destroy_vcpu() to undo what it set up itself. Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> --- xen/common/sched/core.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/xen/common/sched/core.c b/xen/common/sched/core.c index d542c76543..f3ae9998ef 100644 --- a/xen/common/sched/core.c +++ b/xen/common/sched/core.c @@ -589,6 +589,9 @@ int sched_init_vcpu(struct vcpu *v) unit->priv = sched_alloc_udata(dom_scheduler(d), unit, d->sched_priv); if ( unit->priv == NULL ) { + kill_timer(&v->periodic_timer); + kill_timer(&v->singleshot_timer); + kill_timer(&v->poll_timer); sched_free_unit(unit, v); rcu_read_unlock(&sched_res_rculock); return 1; -- 2.34.1 ^ permalink raw reply related [flat|nested] 14+ messages in thread
* Re: [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure 2026-08-18 6:32 ` [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure Furkan Caliskan @ 2026-08-18 7:23 ` Jürgen Groß 0 siblings, 0 replies; 14+ messages in thread From: Jürgen Groß @ 2026-08-18 7:23 UTC (permalink / raw) To: Furkan Caliskan, xen-devel; +Cc: jbeulich, andrew.cooper3, dfaggioli, gwd [-- Attachment #1.1.1: Type: text/plain, Size: 1403 bytes --] On 18.08.26 08:32, Furkan Caliskan wrote: > sched_init_vcpu() calls init_timer() for a vcpu's periodic_timer, > singleshot_timer and poll_timer before it can fail -- these > become live, linked into their target pCPU's per-cpu timer list > regardless of what happens next. If the sched_alloc_udata() call > further down then fails, the function frees the sched_unit via > sched_free_unit() and returns 1, but never unlinks these three > timers. > > The caller, vcpu_create(), does worse: on sched_init_vcpu() > returning nonzero it jumps to fail_wq, skipping fail_sched and > thus sched_destroy_vcpu() -- the only function on this path that > calls kill_timer() on them. vcpu_destroy() then frees the vcpu, > and the three timers embedded in it, while they are still linked > into that shared list. > > This silently corrupts that list. It only shows up later, when > something else touches a neighboring timer: sched_move_domain() > crashed with "Assertion 'entry->prev->next == entry' failed" on a > completely unrelated, valid vcpus's timer. > > Kill all three timers in sched_init_vcpu()'s own failure branch, > so it doesn't depend on the caller reaching sched_destroy_vcpu() > to undo what it set up itself. > > Signed-off-by: Furkan Caliskan <frn1furkan10@gmail.com> Apart from the missing Fixes: tag: Reviewed-by: Juergen Gross <jgross@suse.com> Juergen [-- Attachment #1.1.2: OpenPGP public key --] [-- Type: application/pgp-keys, Size: 3743 bytes --] [-- Attachment #2: OpenPGP digital signature --] [-- Type: application/pgp-signature, Size: 495 bytes --] ^ permalink raw reply [flat|nested] 14+ messages in thread
* Re: [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails 2026-08-18 6:32 [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Furkan Caliskan 2026-08-18 6:32 ` [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() Furkan Caliskan 2026-08-18 6:32 ` [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure Furkan Caliskan @ 2026-08-18 6:48 ` Jan Beulich 2 siblings, 0 replies; 14+ messages in thread From: Jan Beulich @ 2026-08-18 6:48 UTC (permalink / raw) To: Furkan Caliskan; +Cc: jgross, andrew.cooper3, dfaggioli, gwd, xen-devel On 18.08.2026 08:32, Furkan Caliskan wrote: > Furkan Caliskan (2): > xen/sched: core: skip missing vcpu slots in sched_move_domain() > xen/sched: core: kill unarmed timers on sched_init_vcpu() failure > > xen/common/sched/core.c | 22 ++++++++++++++++++++++ > 1 file changed, 22 insertions(+) Just one formal remark: Both patches look to be in want of Fixes: tags. Jan ^ permalink raw reply [flat|nested] 14+ messages in thread
end of thread, other threads:[~2026-08-18 12:20 UTC | newest] Thread overview: 14+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-08-18 6:32 [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Furkan Caliskan 2026-08-18 6:32 ` [PATCH 1/2] xen/sched: core: skip missing vcpu slots in sched_move_domain() Furkan Caliskan 2026-08-18 7:11 ` Jürgen Groß 2026-08-18 7:53 ` Furkan Çalışkan 2026-08-18 10:04 ` Andrew Cooper 2026-08-18 10:13 ` Jürgen Groß 2026-08-18 10:35 ` Andrew Cooper 2026-08-18 10:47 ` Juergen Gross 2026-08-18 10:53 ` Jan Beulich 2026-08-18 12:12 ` Furkan Çalışkan 2026-08-18 12:19 ` Jan Beulich 2026-08-18 6:32 ` [PATCH 2/2] xen/sched: core: kill unarmed timers on sched_init_vcpu() failure Furkan Caliskan 2026-08-18 7:23 ` Jürgen Groß 2026-08-18 6:48 ` [PATCH 0/2] xen/sched: fix crashes when vcpu creation fails Jan Beulich
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.