XEN__READCONSOLE |
-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA256
Xen Security Advisory CVE-2026-62426,CVE-2026-62427 / XSA-499
version 2
sysctl and platform-op locks open to abuse
UPDATES IN VERSION 2
====================
Add further tags to patches.
Public release.
ISSUE DESCRIPTION
=================
To manage the system, sysctl and platform operations are used by the
control domain or a possible Xenstore domain. Some of these operations
may not be executed in parallel, so a system-wide lock each is used.
The way those locks are acquired is, however, not providing any fairness.
Furthermore, with XSM/Flask in use, the lock acquire will, for some
operations, occur ahead of any permission checking.
The sysctl issue is CVE-2026-62426.
The platform-op issue is CVE-2026-62427.
IMPACT
======
A less privileged entity may stall an equally or more privileged entity,
potentially leading to a Denial od Service (DoS) of up to the entire
host.
VULNERABLE SYSTEMS
==================
All Xen versions from 4.0 onwards are vulnerable. Earlier versions use
a different locking operation, but may also be vulnerable.
MITIGATION
==========
There is no known mitigation.
CREDITS
=======
This issue was discovered by Jan Beulich of SUSE.
RESOLUTION
==========
Applying the appropriate set of attached patches resolves this issue.
NOTE: The patches include an adjustment to the default Flask policy. When
custom policies are in use, a respective change will need making there.
Note that patches for released versions are generally prepared to
apply to the stable branches, and may not apply cleanly to the most
recent release tarball. Downstreams are encouraged to update to the
tip of the stable branch before applying these patches.
xsa499/xsa499-?.patch xen-unstable - Xen 4.21.x
xsa499/xsa499-4.20-?.patch Xen 4.20.x - Xen 4.19.x
xsa499/xsa499-4.18-?.patch Xen 4.18.x
xsa499/xsa499-4.17-?.patch Xen 4.17.x
$ sha256sum xsa499*/*
1b716186d37cb6be7b2917bc4801a5d431111f6a01f6e710e2899b6fbf91d747 xsa499/xsa499-1.patch
7789699993f993aa0f118437b9a18c2207cf29491fa08f21e029aa4293698d38 xsa499/xsa499-2.patch
4860a286a7161a407a7e7dc95ecb04a453847295ca73f91f3bd07c3e9e94b7fe xsa499/xsa499-3.patch
fd59436b871caad09017da250996968797ada23223b1e76f9a2e80ae814d63ca xsa499/xsa499-4.17-1.patch
9f83e4866fd20a3e45ecf40a80898446d243444fdf4c17eb695ff7f3d3e9f816 xsa499/xsa499-4.17-2.patch
e7e4fcb7502deb605a854ce451403237ea295394dc30f771001680cc3aaa8f9e xsa499/xsa499-4.17-3.patch
3a979d5c59d690313ec9c3d82d8859dadd6d106296cbf6fe75ef9b42f2626bb5 xsa499/xsa499-4.17-4.patch
d2b3bcad9eb0d82f16befff35ef8fa0d8ed6d06b7e7e2c360003f1eee14d26b2 xsa499/xsa499-4.17-5.patch
ac1f4d1935dc7546c32e5b9c42ebd08d71ca232e559a7021e3ed685c0973675e xsa499/xsa499-4.17-6.patch
143a9b46b3edf689e8b75fb87d1e15b0f0b96baf48547f6d77c00ba5e90ea5ac xsa499/xsa499-4.17-7.patch
cd51ddc70ac765ef00a404d3a21c04a0e234ebca1904159820504e10f70e3812 xsa499/xsa499-4.18-1.patch
32f7827da6a6cdff848a1b620dd1a4d4861008f53d31050c6cde1d353794d22f xsa499/xsa499-4.18-2.patch
3ed288dd99051ebb014cf1cd124d5ed0837935bb0d56963f54699a9e3a966c4e xsa499/xsa499-4.18-3.patch
86fecda24801913f408f86db0b02d0017e870fa95e6edfe5d414343c89b29b37 xsa499/xsa499-4.18-4.patch
055c9114e0c1e789ee510dfb739bf8067a7acbdad6b6edaf2c0e7ed3daac2b81 xsa499/xsa499-4.18-5.patch
211b7410562f438e82266d3101beaef32d66c54b4681a370dd3e47abeec7c5c5 xsa499/xsa499-4.18-6.patch
a0849d860536a199ebeabb6199012d9eae70ee2ed41573765af50aec7b7e7f85 xsa499/xsa499-4.18-7.patch
48b96df5981193f182eda567ad65f1343718656a36ea1ef2d24bdc534ea4ead1 xsa499/xsa499-4.20-1.patch
3e9a315184f64d41011721b2f09275fcc6969d46be3c6f6b788b65fe78c1a0a8 xsa499/xsa499-4.20-2.patch
cf66937eecf0b8a559e60553c21de0e3767270df022c7098d7dd94a46c5ad176 xsa499/xsa499-4.20-3.patch
8932a1480b7431a0ebb048312dda49d5b7d06a5d2b57efb04df0b90bc81e95be xsa499/xsa499-4.20-4.patch
6777a824bc0248f8e12a71f87b60a3209a7a74a08fe7e072d26f80095d188a01 xsa499/xsa499-4.20-5.patch
3ab8c66b133d2b942f7caad0251d427076ad2ef38bcbf6672a937c1406f098ac xsa499/xsa499-4.20-6.patch
1254cc886eb62d65624bcc417701c119e02cc35d31f62e3dda1bda062e170446 xsa499/xsa499-4.20-7.patch
70c315beca0afa411c9e31117d8e0b22b36b5cd70e8d9d2243f5501167a2c631 xsa499/xsa499-4.patch
d0ff37c4807445bd5146057ddaed31d7ce5a0fe6abf413a04f4bceca8419baeb xsa499/xsa499-5.patch
961d31a715ae2c8dd25b74243a6c759ce8c43409c4156ae6bbdcf3faac03f592 xsa499/xsa499-6.patch
60cb3b1e4beef97d633a0156feb0f4f10ac8d21c54152969f5b7a77ecc4a24d3 xsa499/xsa499-7.patch
$
DEPLOYMENT DURING EMBARGO
=========================
Deployment of the patches and/or mitigations described above (or
others which are substantially similar) is permitted during the
embargo, even on public-facing systems with untrusted guest users and
administrators.
But: Distribution of updated software is prohibited (except to other
members of the predisclosure list).
Predisclosure list members who wish to deploy significantly different
patches and/or mitigations, please contact the Xen Project Security
Team.
(Note: this during-embargo deployment notice is retained in
post-embargo publicly released Xen Project advisories, even though it
is then no longer applicable. This is to enable the community to have
oversight of the Xen Project Security Team's decisionmaking.)
For more information about permissible uses of embargoed information,
consult the Xen Project community's agreed Security Policy:
http://www.xenproject.org/security-policy.html
-----BEGIN PGP SIGNATURE-----
iQFABAEBCAAqFiEEI+MiLBRfRHX6gGCng/4UyVfoK9kFAmpomq8MHHBncEB4ZW4u
b3JnAAoJEIP+FMlX6CvZ9AcH/3XOTFbTvmwW48Q6nVkwvys5fiU2esrkFOTK96nn
p55QQoVVjebet7N+eRtezkjfHGCqphGOuHcAJ0uMlNNgUfhXNxxu6qaqMHX5h29P
DqQKZzOKuQM1frcyupJdhi8dbNxan1pXMv6rJAcWpmBoPEt9e+qwpnWtBX/HPH3b
66cQwLhmenlqwZak7E0bsLFw1A6WUXs1VS4xeiOE3Cj91F5hPUvqkZGI0pNbNAIn
6copsM0UAcTkwGrwIvKuN5b3BqFmaCWvGBfqsuNpj5MnJddigV68UeylMepFjoDH
Urn2qiOTcJKxdFt/AUX57AqiBF2MtDbi49RLLxsTCJvYvFM=
=R0It
-----END PGP SIGNATURE-----
From: Jan Beulich <jbeulich@suse.com>
Subject: platform-op/XSM: move resource-{,un}plug-core checks
Integrate the checking with flask_platform_op(); there never really was a
need to defer these checks, as the sub-op has always been known to the
function. As a positive side effect, permissions are then checked at the
same early point with and without Flask.
This is CVE-2026-62427 / part of XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -735,10 +735,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu >= nr_cpu_ids || !cpu_present(cpu) ||
clocksource_is_tsc() )
{
@@ -761,10 +757,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_unplug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu == 0 )
{
ret = -EOPNOTSUPP;
@@ -789,20 +781,12 @@ ret_t do_platform_op(
}
case XENPF_cpu_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = cpu_add(op->u.cpu_add.apic_id,
op->u.cpu_add.acpi_id,
op->u.cpu_add.pxm);
break;
case XENPF_mem_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = memory_add(op->u.mem_add.spfn,
op->u.mem_add.epfn,
op->u.mem_add.pxm);
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1207,6 +1207,7 @@ static int cf_check flask_pci_config_per
}
+#if defined(CONFIG_SYSCTL) || defined(CONFIG_X86)
static int cf_check flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
@@ -1216,6 +1217,7 @@ static int cf_check flask_resource_unplu
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
+#endif /* CONFIG_SYSCTL || CONFIG_X86 */
#ifdef CONFIG_SYSCTL
static int flask_resource_use_core(void)
@@ -1536,12 +1538,13 @@ static int cf_check flask_platform_op(ui
switch ( op )
{
#ifdef CONFIG_X86
- /* These operations have their own XSM hooks */
case XENPF_cpu_online:
- case XENPF_cpu_offline:
case XENPF_cpu_hotadd:
case XENPF_mem_hotadd:
- return 0;
+ return flask_resource_plug_core();
+
+ case XENPF_cpu_offline:
+ return flask_resource_unplug_core();
#endif
case XENPF_settime32:
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: pass full struct xen_sysctl to xsm_sysctl()
Subsequently some sub-ops will want to inspect their sub-sub-ops.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -42,7 +42,7 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->interface_version != XEN_SYSCTL_INTERFACE_VERSION )
return -EACCES;
- ret = xsm_sysctl(XSM_PRIV, op->cmd);
+ ret = xsm_sysctl(XSM_PRIV, op);
if ( ret )
return ret;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -180,7 +180,8 @@ static XSM_INLINE int cf_check xsm_domct
}
}
-static XSM_INLINE int cf_check xsm_sysctl(XSM_DEFAULT_ARG int cmd)
+static XSM_INLINE int cf_check xsm_sysctl(
+ XSM_DEFAULT_ARG const struct xen_sysctl *op)
{
XSM_ASSERT_ACTION(XSM_PRIV);
return xsm_default_action(action, current->domain, NULL);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -62,7 +62,7 @@ struct xsm_ops {
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
#ifdef CONFIG_SYSCTL
- int (*sysctl)(int cmd);
+ int (*sysctl)(const struct xen_sysctl *op);
int (*readconsole)(uint32_t clear);
#endif
@@ -251,9 +251,9 @@ static inline int xsm_domctl(xsm_default
}
#ifdef CONFIG_SYSCTL
-static inline int xsm_sysctl(xsm_default_t def, int cmd)
+static inline int xsm_sysctl(xsm_default_t def, const struct xen_sysctl *op)
{
- return alternative_call(xsm_ops.sysctl, cmd);
+ return alternative_call(xsm_ops.sysctl, op);
}
static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -872,9 +872,9 @@ static int cf_check flask_domctl(struct
}
#ifdef CONFIG_SYSCTL
-static int cf_check flask_sysctl(int cmd)
+static int cf_check flask_sysctl(const struct xen_sysctl *op)
{
- switch ( cmd )
+ switch ( op->cmd )
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
@@ -942,7 +942,7 @@ static int cf_check flask_sysctl(int cmd
XEN2__COVERAGE_OP, NULL);
default:
- return avc_unknown_permission("sysctl", cmd);
+ return avc_unknown_permission("sysctl", op->cmd);
}
}
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .scheduler_op() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -2122,10 +2122,6 @@ long sched_adjust_global(struct xen_sysc
struct cpupool *pool;
int rc;
- rc = xsm_sysctl_scheduler_op(XSM_HOOK, op->cmd);
- if ( rc )
- return rc;
-
if ( (op->cmd != XEN_SYSCTL_SCHEDOP_putinfo) &&
(op->cmd != XEN_SYSCTL_SCHEDOP_getinfo) )
return -EINVAL;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -141,12 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo
return xsm_default_action(action, current->domain, d);
}
-static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_set_target(
XSM_DEFAULT_ARG struct domain *d, struct domain *e)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -56,9 +56,6 @@ struct xsm_ops {
struct xen_domctl_getdomaininfo *info);
int (*domain_create)(struct domain *d, uint32_t ssidref);
int (*getdomaininfo)(struct domain *d);
-#ifdef CONFIG_SYSCTL
- int (*sysctl_scheduler_op)(int op);
-#endif
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
#ifdef CONFIG_SYSCTL
@@ -231,13 +228,6 @@ static inline int xsm_get_domain_state(x
return alternative_call(xsm_ops.get_domain_state, d);
}
-#ifdef CONFIG_SYSCTL
-static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd)
-{
- return alternative_call(xsm_ops.sysctl_scheduler_op, cmd);
-}
-#endif
-
static inline int xsm_set_target(
xsm_default_t def, struct domain *d, struct domain *e)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -18,9 +18,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = xsm_security_domaininfo,
.domain_create = xsm_domain_create,
.getdomaininfo = xsm_getdomaininfo,
-#ifdef CONFIG_SYSCTL
- .sysctl_scheduler_op = xsm_sysctl_scheduler_op,
-#endif
.set_target = xsm_set_target,
.domctl = xsm_domctl,
#ifdef CONFIG_SYSCTL
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -606,7 +606,7 @@ static int flask_domctl_scheduler_op(str
}
#ifdef CONFIG_SYSCTL
-static int cf_check flask_sysctl_scheduler_op(int op)
+static int flask_sysctl_scheduler_op(unsigned int op)
{
switch ( op )
{
@@ -880,7 +880,6 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
- case XEN_SYSCTL_scheduler_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
@@ -916,6 +915,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_cpupool_op:
return domain_has_xen(current->domain, XEN__CPUPOOL_OP);
+ case XEN_SYSCTL_scheduler_op:
+ return flask_sysctl_scheduler_op(op->u.scheduler_op.cmd);
+
case XEN_SYSCTL_physinfo:
case XEN_SYSCTL_cputopoinfo:
case XEN_SYSCTL_numainfo:
@@ -1895,9 +1897,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = flask_security_domaininfo,
.domain_create = flask_domain_create,
.getdomaininfo = flask_getdomaininfo,
-#ifdef CONFIG_SYSCTL
- .sysctl_scheduler_op = flask_sysctl_scheduler_op,
-#endif
.set_target = flask_set_target,
.domctl = flask_domctl,
#ifdef CONFIG_SYSCTL
From: Jan Beulich <jbeulich@suse.com>
Subject: platform-op/XSM: move resource-{,un}plug-core checks
Integrate the checking with flask_platform_op(); there never really was a
need to defer these checks, as the sub-op has always been known to the
function. As a positive side effect, permissions are then checked at the
same early point with and without Flask.
This is CVE-2026-62427 / part of XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -644,10 +644,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu >= nr_cpu_ids || !cpu_present(cpu) ||
clocksource_is_tsc() )
{
@@ -670,10 +666,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_unplug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu == 0 )
{
ret = -EOPNOTSUPP;
@@ -699,20 +691,12 @@ ret_t do_platform_op(
break;
case XENPF_cpu_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = cpu_add(op->u.cpu_add.apic_id,
op->u.cpu_add.acpi_id,
op->u.cpu_add.pxm);
- break;
+ break;
case XENPF_mem_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = memory_add(op->u.mem_add.spfn,
op->u.mem_add.epfn,
op->u.mem_add.pxm);
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1551,12 +1551,13 @@ static int cf_check flask_platform_op(ui
switch ( op )
{
#ifdef CONFIG_X86
- /* These operations have their own XSM hooks */
case XENPF_cpu_online:
- case XENPF_cpu_offline:
case XENPF_cpu_hotadd:
case XENPF_mem_hotadd:
- return 0;
+ return flask_resource_plug_core();
+
+ case XENPF_cpu_offline:
+ return flask_resource_unplug_core();
#endif
case XENPF_settime32:
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: pass full struct xen_sysctl to xsm_sysctl()
Subsequently some sub-ops will want to inspect their sub-sub-ops.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -42,7 +42,7 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->interface_version != XEN_SYSCTL_INTERFACE_VERSION )
return -EACCES;
- ret = xsm_sysctl(XSM_PRIV, op->cmd);
+ ret = xsm_sysctl(XSM_PRIV, op);
if ( ret )
return ret;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -178,7 +178,8 @@ static XSM_INLINE int cf_check xsm_domct
}
}
-static XSM_INLINE int cf_check xsm_sysctl(XSM_DEFAULT_ARG int cmd)
+static XSM_INLINE int cf_check xsm_sysctl(
+ XSM_DEFAULT_ARG const struct xen_sysctl *op)
{
XSM_ASSERT_ACTION(XSM_PRIV);
return xsm_default_action(action, current->domain, NULL);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -60,7 +60,7 @@ struct xsm_ops {
int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
- int (*sysctl)(int cmd);
+ int (*sysctl)(const struct xen_sysctl *op);
int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
@@ -237,9 +237,9 @@ static inline int xsm_domctl(xsm_default
return alternative_call(xsm_ops.domctl, d, op);
}
-static inline int xsm_sysctl(xsm_default_t def, int cmd)
+static inline int xsm_sysctl(xsm_default_t def, const struct xen_sysctl *op)
{
- return alternative_call(xsm_ops.sysctl, cmd);
+ return alternative_call(xsm_ops.sysctl, op);
}
static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -889,9 +889,9 @@ static int cf_check flask_domctl(struct
}
}
-static int cf_check flask_sysctl(int cmd)
+static int cf_check flask_sysctl(const struct xen_sysctl *op)
{
- switch ( cmd )
+ switch ( op->cmd )
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
@@ -963,7 +963,7 @@ static int cf_check flask_sysctl(int cmd
XEN2__COVERAGE_OP, NULL);
default:
- return avc_unknown_permission("sysctl", cmd);
+ return avc_unknown_permission("sysctl", op->cmd);
}
}
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .scheduler_op() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -2089,10 +2089,6 @@ long sched_adjust_global(struct xen_sysc
struct cpupool *pool;
int rc;
- rc = xsm_sysctl_scheduler_op(XSM_HOOK, op->cmd);
- if ( rc )
- return rc;
-
if ( (op->cmd != XEN_SYSCTL_SCHEDOP_putinfo) &&
(op->cmd != XEN_SYSCTL_SCHEDOP_getinfo) )
return -EINVAL;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -141,12 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo
return xsm_default_action(action, current->domain, d);
}
-static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_set_target(
XSM_DEFAULT_ARG struct domain *d, struct domain *e)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -57,7 +57,6 @@ struct xsm_ops {
struct xen_domctl_getdomaininfo *info);
int (*domain_create)(struct domain *d, uint32_t ssidref);
int (*getdomaininfo)(struct domain *d);
- int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
@@ -220,11 +219,6 @@ static inline int xsm_getdomaininfo(xsm_
return alternative_call(xsm_ops.getdomaininfo, d);
}
-static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd)
-{
- return alternative_call(xsm_ops.sysctl_scheduler_op, cmd);
-}
-
static inline int xsm_set_target(
xsm_default_t def, struct domain *d, struct domain *e)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -18,7 +18,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = xsm_security_domaininfo,
.domain_create = xsm_domain_create,
.getdomaininfo = xsm_getdomaininfo,
- .sysctl_scheduler_op = xsm_sysctl_scheduler_op,
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -636,7 +636,7 @@ static int flask_domctl_scheduler_op(str
}
}
-static int cf_check flask_sysctl_scheduler_op(int op)
+static int flask_sysctl_scheduler_op(unsigned int op)
{
switch ( op )
{
@@ -897,7 +897,6 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
- case XEN_SYSCTL_scheduler_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
@@ -933,6 +932,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_cpupool_op:
return domain_has_xen(current->domain, XEN__CPUPOOL_OP);
+ case XEN_SYSCTL_scheduler_op:
+ return flask_sysctl_scheduler_op(op->u.scheduler_op.cmd);
+
case XEN_SYSCTL_physinfo:
case XEN_SYSCTL_cputopoinfo:
case XEN_SYSCTL_numainfo:
@@ -1895,7 +1897,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = flask_security_domaininfo,
.domain_create = flask_domain_create,
.getdomaininfo = flask_getdomaininfo,
- .sysctl_scheduler_op = flask_sysctl_scheduler_op,
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/Flask: add preliminary check for XEN_SYSCTL_getdomaininfolist
To shield the sysctl lock from abuse by unauthorized domains, follow what
5154fdda1124 ("domctl: protect locking for get_domain_state") did: Check
for permission to issue the operation against DOM_XEN in flask_sysctl().
The finer-grained xsm_getdomaininfo() later in the handling of the sub-op
remains unaltered.
In the in-tree policy respective permission therefore needs granting.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/tools/flask/policy/modules/xen.if
+++ b/tools/flask/policy/modules/xen.if
@@ -90,6 +90,7 @@ define(`create_domain_build_label', `
# manage_domain(priv, target)
# Allow managing a running domain
define(`manage_domain', `
+ allow $1 domxen_t:domain getdomaininfo;
allow $1 $2:domain { getdomaininfo getvcpuinfo getaffinity
getaddrsize pause unpause trigger shutdown destroy
setaffinity setdomainmaxmem getscheduler resume
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -895,7 +895,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
- case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
@@ -942,6 +941,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_get_cpu_policy:
return domain_has_xen(current->domain, XEN__PHYSINFO);
+ case XEN_SYSCTL_getdomaininfolist:
+ return flask_getdomaininfo(dom_xen);
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .readconsole() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -59,10 +59,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
switch ( op->cmd )
{
case XEN_SYSCTL_readconsole:
- ret = xsm_readconsole(XSM_HOOK, op->u.readconsole.clear);
- if ( ret )
- break;
-
ret = read_console_ring(&op->u.readconsole);
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -179,12 +179,6 @@ static XSM_INLINE int cf_check xsm_sysct
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_readconsole(XSM_DEFAULT_ARG uint32_t clear)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_alloc_security_domain(struct domain *d)
{
return 0;
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -60,7 +60,6 @@ struct xsm_ops {
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
- int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
int (*evtchn_interdomain)(struct domain *d1, struct evtchn *chn1,
@@ -236,11 +235,6 @@ static inline int xsm_sysctl(xsm_default
return alternative_call(xsm_ops.sysctl, op);
}
-static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
-{
- return alternative_call(xsm_ops.readconsole, clear);
-}
-
static inline int xsm_evtchn_unbound(
xsm_default_t def, struct domain *d1, struct evtchn *chn, domid_t id2)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -21,7 +21,6 @@ static const struct xsm_ops __initconst_
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
- .readconsole = xsm_readconsole,
.evtchn_unbound = xsm_evtchn_unbound,
.evtchn_interdomain = xsm_evtchn_interdomain,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -894,13 +894,18 @@ static int cf_check flask_sysctl(const s
switch ( op->cmd )
{
/* These have individual XSM hooks */
- case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
return 0;
+ case XEN_SYSCTL_readconsole:
+ return domain_has_xen(current->domain,
+ XEN__READCONSOLE |
+ (op->u.readconsole.clear ? XEN__CLEARCONSOLE
+ : 0));
+
case XEN_SYSCTL_tbuf_op:
return domain_has_xen(current->domain, XEN__TBUFCONTROL);
@@ -971,16 +976,6 @@ static int cf_check flask_sysctl(const s
}
}
-static int cf_check flask_readconsole(uint32_t clear)
-{
- uint32_t perms = XEN__READCONSOLE;
-
- if ( clear )
- perms |= XEN__CLEARCONSOLE;
-
- return domain_has_xen(current->domain, perms);
-}
-
static inline uint32_t resource_to_perm(uint8_t access)
{
if ( access )
@@ -1902,7 +1897,6 @@ static const struct xsm_ops __initconst_
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
- .readconsole = flask_readconsole,
.evtchn_unbound = flask_evtchn_unbound,
.evtchn_interdomain = flask_evtchn_interdomain,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .resource_{,un}plug_core() hooks
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask. Note that these were x86-
only, i.e. some dead/unreachable code gets eliminated for (in particular)
Arm.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/sysctl.c
+++ b/xen/arch/x86/sysctl.c
@@ -118,20 +118,17 @@ long arch_do_sysctl(
{
unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
unsigned int op = sysctl->u.cpu_hotplug.op;
- bool plug;
long (*fn)(void *);
void *hcpu;
switch ( op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
- plug = true;
fn = cpu_up_helper;
hcpu = _p(cpu);
break;
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
- plug = false;
fn = cpu_down_helper;
hcpu = _p(cpu);
break;
@@ -151,9 +148,8 @@ long arch_do_sysctl(
if ( CONFIG_NR_CPUS <= 1 )
/* Mimic behavior of smt_up_down_helper(). */
return 0;
- plug = op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE;
fn = smt_up_down_helper;
- hcpu = _p(plug);
+ hcpu = _p(op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE);
break;
default:
@@ -162,10 +158,6 @@ long arch_do_sysctl(
}
if ( !ret )
- ret = plug ? xsm_resource_plug_core(XSM_HOOK)
- : xsm_resource_unplug_core(XSM_HOOK);
-
- if ( !ret )
ret = continue_hypercall_on_cpu(0, fn, hcpu);
}
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -395,18 +395,6 @@ static XSM_INLINE int cf_check xsm_get_d
}
#endif /* HAS_PASSTHROUGH && HAS_PCI */
-static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
-static XSM_INLINE int cf_check xsm_resource_unplug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_resource_plug_pci(
XSM_DEFAULT_ARG uint32_t machine_bdf)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -121,8 +121,6 @@ struct xsm_ops {
int (*get_device_group)(uint32_t machine_bdf);
#endif
- int (*resource_plug_core)(void);
- int (*resource_unplug_core)(void);
int (*resource_plug_pci)(uint32_t machine_bdf);
int (*resource_unplug_pci)(uint32_t machine_bdf);
int (*resource_setup_pci)(uint32_t machine_bdf);
@@ -500,16 +498,6 @@ static inline int xsm_resource_unplug_pc
return alternative_call(xsm_ops.resource_unplug_pci, machine_bdf);
}
-static inline int xsm_resource_plug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_plug_core);
-}
-
-static inline int xsm_resource_unplug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_unplug_core);
-}
-
static inline int xsm_resource_setup_pci(
xsm_default_t def, uint32_t machine_bdf)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -76,8 +76,6 @@ static const struct xsm_ops __initconst_
.get_device_group = xsm_get_device_group,
#endif
- .resource_plug_core = xsm_resource_plug_core,
- .resource_unplug_core = xsm_resource_unplug_core,
.resource_plug_pci = xsm_resource_plug_pci,
.resource_unplug_pci = xsm_resource_unplug_pci,
.resource_setup_pci = xsm_resource_setup_pci,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -56,6 +56,9 @@ static int flask_deassign_dtdevice(struc
#endif
#endif /* CONFIG_HAS_PASSTHROUGH */
+static int flask_resource_plug_core(void);
+static int flask_resource_unplug_core(void);
+
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
@@ -895,9 +898,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_page_offline_op:
-#ifdef CONFIG_X86
- case XEN_SYSCTL_cpu_hotplug:
-#endif
return 0;
case XEN_SYSCTL_readconsole:
@@ -949,6 +949,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_getdomaininfolist:
return flask_getdomaininfo(dom_xen);
+#ifdef CONFIG_X86
+ case XEN_SYSCTL_cpu_hotplug:
+ switch ( op->u.cpu_hotplug.op )
+ {
+ case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
+ return flask_resource_plug_core();
+
+ case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
+ return flask_resource_unplug_core();
+
+ default:
+ return avc_unknown_permission("cpu_hotplug", op->u.cpu_hotplug.op);
+ }
+#endif
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
@@ -1226,12 +1243,12 @@ static int cf_check flask_pci_config_per
}
-static int cf_check flask_resource_plug_core(void)
+static int flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
}
-static int cf_check flask_resource_unplug_core(void)
+static int flask_resource_unplug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
@@ -1947,8 +1964,6 @@ static const struct xsm_ops __initconst_
.iomem_mapping = flask_iomem_mapping,
.pci_config_permission = flask_pci_config_permission,
- .resource_plug_core = flask_resource_plug_core,
- .resource_unplug_core = flask_resource_unplug_core,
.resource_plug_pci = flask_resource_plug_pci,
.resource_unplug_pci = flask_resource_unplug_pci,
.resource_setup_pci = flask_resource_setup_pci,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .page_offline() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -187,10 +187,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->u.page_offline.end < op->u.page_offline.start )
break;
- ret = xsm_page_offline(XSM_HOOK, op->u.page_offline.cmd);
- if ( ret )
- break;
-
ptr = status = xmalloc_array(uint32_t,
(op->u.page_offline.end -
op->u.page_offline.start + 1));
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -428,12 +428,6 @@ static XSM_INLINE int cf_check xsm_resou
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_page_offline(XSM_DEFAULT_ARG uint32_t cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_hypfs_op(XSM_DEFAULT_VOID)
{
XSM_ASSERT_ACTION(XSM_PRIV);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -127,7 +127,6 @@ struct xsm_ops {
int (*resource_setup_gsi)(int gsi);
int (*resource_setup_misc)(void);
- int (*page_offline)(uint32_t cmd);
int (*hypfs_op)(void);
long (*do_xsm_op)(XEN_GUEST_HANDLE_PARAM(void) op);
@@ -514,11 +513,6 @@ static inline int xsm_resource_setup_mis
return alternative_call(xsm_ops.resource_setup_misc);
}
-static inline int xsm_page_offline(xsm_default_t def, uint32_t cmd)
-{
- return alternative_call(xsm_ops.page_offline, cmd);
-}
-
static inline int xsm_hypfs_op(xsm_default_t def)
{
return alternative_call(xsm_ops.hypfs_op);
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -82,7 +82,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = xsm_resource_setup_gsi,
.resource_setup_misc = xsm_resource_setup_misc,
- .page_offline = xsm_page_offline,
.hypfs_op = xsm_hypfs_op,
.hvm_param = xsm_hvm_param,
.hvm_param_altp2mhvm = xsm_hvm_param_altp2mhvm,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -58,6 +58,7 @@ static int flask_deassign_dtdevice(struc
static int flask_resource_plug_core(void);
static int flask_resource_unplug_core(void);
+static int flask_resource_use_core(void);
static uint32_t domain_sid(const struct domain *dom)
{
@@ -896,10 +897,6 @@ static int cf_check flask_sysctl(const s
{
switch ( op->cmd )
{
- /* These have individual XSM hooks */
- case XEN_SYSCTL_page_offline_op:
- return 0;
-
case XEN_SYSCTL_readconsole:
return domain_has_xen(current->domain,
XEN__READCONSOLE |
@@ -930,6 +927,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_pm_op:
return domain_has_xen(current->domain, XEN__PM_OP);
+ case XEN_SYSCTL_page_offline_op:
+ switch ( op->u.page_offline.cmd )
+ {
+ case sysctl_page_offline:
+ return flask_resource_unplug_core();
+
+ case sysctl_page_online:
+ return flask_resource_plug_core();
+
+ case sysctl_query_page_offline:
+ return flask_resource_use_core();
+
+ default:
+ return avc_unknown_permission("page_offline",
+ op->u.page_offline.cmd);
+ }
+
case XEN_SYSCTL_lockprof_op:
return domain_has_xen(current->domain, XEN__LOCKPROF);
@@ -1321,21 +1335,6 @@ static int cf_check flask_resource_setup
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_RESOURCE, RESOURCE__SETUP, NULL);
}
-static inline int cf_check flask_page_offline(uint32_t cmd)
-{
- switch ( cmd )
- {
- case sysctl_page_offline:
- return flask_resource_unplug_core();
- case sysctl_page_online:
- return flask_resource_plug_core();
- case sysctl_query_page_offline:
- return flask_resource_use_core();
- default:
- return avc_unknown_permission("page_offline", cmd);
- }
-}
-
static inline int cf_check flask_hypfs_op(void)
{
return domain_has_xen(current->domain, XEN__HYPFS_OP);
@@ -1970,7 +1969,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = flask_resource_setup_gsi,
.resource_setup_misc = flask_resource_setup_misc,
- .page_offline = flask_page_offline,
.hypfs_op = flask_hypfs_op,
.hvm_param = flask_hvm_param,
.hvm_param_altp2mhvm = flask_hvm_param_altp2mhvm,
From: Jan Beulich <jbeulich@suse.com>
Subject: platform-op/XSM: move resource-{,un}plug-core checks
Integrate the checking with flask_platform_op(); there never really was a
need to defer these checks, as the sub-op has always been known to the
function. As a positive side effect, permissions are then checked at the
same early point with and without Flask.
This is CVE-2026-62427 / part of XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -673,10 +673,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu >= nr_cpu_ids || !cpu_present(cpu) ||
clocksource_is_tsc() )
{
@@ -699,10 +695,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_unplug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu == 0 )
{
ret = -EOPNOTSUPP;
@@ -728,20 +720,12 @@ ret_t do_platform_op(
break;
case XENPF_cpu_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = cpu_add(op->u.cpu_add.apic_id,
op->u.cpu_add.acpi_id,
op->u.cpu_add.pxm);
- break;
+ break;
case XENPF_mem_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = memory_add(op->u.mem_add.spfn,
op->u.mem_add.epfn,
op->u.mem_add.pxm);
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1554,12 +1554,13 @@ static int cf_check flask_platform_op(ui
switch ( op )
{
#ifdef CONFIG_X86
- /* These operations have their own XSM hooks */
case XENPF_cpu_online:
- case XENPF_cpu_offline:
case XENPF_cpu_hotadd:
case XENPF_mem_hotadd:
- return 0;
+ return flask_resource_plug_core();
+
+ case XENPF_cpu_offline:
+ return flask_resource_unplug_core();
#endif
case XENPF_settime32:
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: pass full struct xen_sysctl to xsm_sysctl()
Subsequently some sub-ops will want to inspect their sub-sub-ops.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -42,7 +42,7 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->interface_version != XEN_SYSCTL_INTERFACE_VERSION )
return -EACCES;
- ret = xsm_sysctl(XSM_PRIV, op->cmd);
+ ret = xsm_sysctl(XSM_PRIV, op);
if ( ret )
return ret;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -178,7 +178,8 @@ static XSM_INLINE int cf_check xsm_domct
}
}
-static XSM_INLINE int cf_check xsm_sysctl(XSM_DEFAULT_ARG int cmd)
+static XSM_INLINE int cf_check xsm_sysctl(
+ XSM_DEFAULT_ARG const struct xen_sysctl *op)
{
XSM_ASSERT_ACTION(XSM_PRIV);
return xsm_default_action(action, current->domain, NULL);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -60,7 +60,7 @@ struct xsm_ops {
int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
- int (*sysctl)(int cmd);
+ int (*sysctl)(const struct xen_sysctl *op);
int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
@@ -239,9 +239,9 @@ static inline int xsm_domctl(xsm_default
return alternative_call(xsm_ops.domctl, d, op);
}
-static inline int xsm_sysctl(xsm_default_t def, int cmd)
+static inline int xsm_sysctl(xsm_default_t def, const struct xen_sysctl *op)
{
- return alternative_call(xsm_ops.sysctl, cmd);
+ return alternative_call(xsm_ops.sysctl, op);
}
static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -892,9 +892,9 @@ static int cf_check flask_domctl(struct
}
}
-static int cf_check flask_sysctl(int cmd)
+static int cf_check flask_sysctl(const struct xen_sysctl *op)
{
- switch ( cmd )
+ switch ( op->cmd )
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
@@ -966,7 +966,7 @@ static int cf_check flask_sysctl(int cmd
XEN2__COVERAGE_OP, NULL);
default:
- return avc_unknown_permission("sysctl", cmd);
+ return avc_unknown_permission("sysctl", op->cmd);
}
}
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .scheduler_op() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -2089,10 +2089,6 @@ long sched_adjust_global(struct xen_sysc
struct cpupool *pool;
int rc;
- rc = xsm_sysctl_scheduler_op(XSM_HOOK, op->cmd);
- if ( rc )
- return rc;
-
if ( (op->cmd != XEN_SYSCTL_SCHEDOP_putinfo) &&
(op->cmd != XEN_SYSCTL_SCHEDOP_getinfo) )
return -EINVAL;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -141,12 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo
return xsm_default_action(action, current->domain, d);
}
-static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_set_target(
XSM_DEFAULT_ARG struct domain *d, struct domain *e)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -57,7 +57,6 @@ struct xsm_ops {
struct xen_domctl_getdomaininfo *info);
int (*domain_create)(struct domain *d, uint32_t ssidref);
int (*getdomaininfo)(struct domain *d);
- int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
@@ -222,11 +221,6 @@ static inline int xsm_getdomaininfo(xsm_
return alternative_call(xsm_ops.getdomaininfo, d);
}
-static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd)
-{
- return alternative_call(xsm_ops.sysctl_scheduler_op, cmd);
-}
-
static inline int xsm_set_target(
xsm_default_t def, struct domain *d, struct domain *e)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -18,7 +18,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = xsm_security_domaininfo,
.domain_create = xsm_domain_create,
.getdomaininfo = xsm_getdomaininfo,
- .sysctl_scheduler_op = xsm_sysctl_scheduler_op,
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -636,7 +636,7 @@ static int flask_domctl_scheduler_op(str
}
}
-static int cf_check flask_sysctl_scheduler_op(int op)
+static int flask_sysctl_scheduler_op(unsigned int op)
{
switch ( op )
{
@@ -900,7 +900,6 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
- case XEN_SYSCTL_scheduler_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
@@ -936,6 +935,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_cpupool_op:
return domain_has_xen(current->domain, XEN__CPUPOOL_OP);
+ case XEN_SYSCTL_scheduler_op:
+ return flask_sysctl_scheduler_op(op->u.scheduler_op.cmd);
+
case XEN_SYSCTL_physinfo:
case XEN_SYSCTL_cputopoinfo:
case XEN_SYSCTL_numainfo:
@@ -1902,7 +1904,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = flask_security_domaininfo,
.domain_create = flask_domain_create,
.getdomaininfo = flask_getdomaininfo,
- .sysctl_scheduler_op = flask_sysctl_scheduler_op,
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/Flask: add preliminary check for XEN_SYSCTL_getdomaininfolist
To shield the sysctl lock from abuse by unauthorized domains, follow what
5154fdda1124 ("domctl: protect locking for get_domain_state") did: Check
for permission to issue the operation against DOM_XEN in flask_sysctl().
The finer-grained xsm_getdomaininfo() later in the handling of the sub-op
remains unaltered.
In the in-tree policy respective permission therefore needs granting.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/tools/flask/policy/modules/xen.if
+++ b/tools/flask/policy/modules/xen.if
@@ -90,6 +90,7 @@ define(`create_domain_build_label', `
# manage_domain(priv, target)
# Allow managing a running domain
define(`manage_domain', `
+ allow $1 domxen_t:domain getdomaininfo;
allow $1 $2:domain { getdomaininfo getvcpuinfo getaffinity
getaddrsize pause unpause trigger shutdown destroy
setaffinity setdomainmaxmem getscheduler resume
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -898,7 +898,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
- case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
@@ -945,6 +944,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_get_cpu_policy:
return domain_has_xen(current->domain, XEN__PHYSINFO);
+ case XEN_SYSCTL_getdomaininfolist:
+ return flask_getdomaininfo(dom_xen);
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .readconsole() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -59,10 +59,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
switch ( op->cmd )
{
case XEN_SYSCTL_readconsole:
- ret = xsm_readconsole(XSM_HOOK, op->u.readconsole.clear);
- if ( ret )
- break;
-
ret = read_console_ring(&op->u.readconsole);
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -179,12 +179,6 @@ static XSM_INLINE int cf_check xsm_sysct
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_readconsole(XSM_DEFAULT_ARG uint32_t clear)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_alloc_security_domain(struct domain *d)
{
return 0;
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -60,7 +60,6 @@ struct xsm_ops {
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
- int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
int (*evtchn_interdomain)(struct domain *d1, struct evtchn *chn1,
@@ -238,11 +237,6 @@ static inline int xsm_sysctl(xsm_default
return alternative_call(xsm_ops.sysctl, op);
}
-static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
-{
- return alternative_call(xsm_ops.readconsole, clear);
-}
-
static inline int xsm_evtchn_unbound(
xsm_default_t def, struct domain *d1, struct evtchn *chn, domid_t id2)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -21,7 +21,6 @@ static const struct xsm_ops __initconst_
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
- .readconsole = xsm_readconsole,
.evtchn_unbound = xsm_evtchn_unbound,
.evtchn_interdomain = xsm_evtchn_interdomain,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -897,13 +897,18 @@ static int cf_check flask_sysctl(const s
switch ( op->cmd )
{
/* These have individual XSM hooks */
- case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
return 0;
+ case XEN_SYSCTL_readconsole:
+ return domain_has_xen(current->domain,
+ XEN__READCONSOLE |
+ (op->u.readconsole.clear ? XEN__CLEARCONSOLE
+ : 0));
+
case XEN_SYSCTL_tbuf_op:
return domain_has_xen(current->domain, XEN__TBUFCONTROL);
@@ -974,16 +979,6 @@ static int cf_check flask_sysctl(const s
}
}
-static int cf_check flask_readconsole(uint32_t clear)
-{
- uint32_t perms = XEN__READCONSOLE;
-
- if ( clear )
- perms |= XEN__CLEARCONSOLE;
-
- return domain_has_xen(current->domain, perms);
-}
-
static inline uint32_t resource_to_perm(uint8_t access)
{
if ( access )
@@ -1909,7 +1904,6 @@ static const struct xsm_ops __initconst_
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
- .readconsole = flask_readconsole,
.evtchn_unbound = flask_evtchn_unbound,
.evtchn_interdomain = flask_evtchn_interdomain,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .resource_{,un}plug_core() hooks
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask. Note that these were x86-
only, i.e. some dead/unreachable code gets eliminated for (in particular)
Arm.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/sysctl.c
+++ b/xen/arch/x86/sysctl.c
@@ -117,20 +117,17 @@ long arch_do_sysctl(
{
unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
unsigned int op = sysctl->u.cpu_hotplug.op;
- bool plug;
long (*fn)(void *);
void *hcpu;
switch ( op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
- plug = true;
fn = cpu_up_helper;
hcpu = _p(cpu);
break;
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
- plug = false;
fn = cpu_down_helper;
hcpu = _p(cpu);
break;
@@ -150,9 +147,8 @@ long arch_do_sysctl(
if ( CONFIG_NR_CPUS <= 1 )
/* Mimic behavior of smt_up_down_helper(). */
return 0;
- plug = op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE;
fn = smt_up_down_helper;
- hcpu = _p(plug);
+ hcpu = _p(op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE);
break;
default:
@@ -161,10 +157,6 @@ long arch_do_sysctl(
}
if ( !ret )
- ret = plug ? xsm_resource_plug_core(XSM_HOOK)
- : xsm_resource_unplug_core(XSM_HOOK);
-
- if ( !ret )
ret = continue_hypercall_on_cpu(0, fn, hcpu);
}
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -395,18 +395,6 @@ static XSM_INLINE int cf_check xsm_get_d
}
#endif /* HAS_PASSTHROUGH && HAS_PCI */
-static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
-static XSM_INLINE int cf_check xsm_resource_unplug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_resource_plug_pci(
XSM_DEFAULT_ARG uint32_t machine_bdf)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -123,8 +123,6 @@ struct xsm_ops {
int (*get_device_group)(uint32_t machine_bdf);
#endif
- int (*resource_plug_core)(void);
- int (*resource_unplug_core)(void);
int (*resource_plug_pci)(uint32_t machine_bdf);
int (*resource_unplug_pci)(uint32_t machine_bdf);
int (*resource_setup_pci)(uint32_t machine_bdf);
@@ -508,16 +506,6 @@ static inline int xsm_resource_unplug_pc
return alternative_call(xsm_ops.resource_unplug_pci, machine_bdf);
}
-static inline int xsm_resource_plug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_plug_core);
-}
-
-static inline int xsm_resource_unplug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_unplug_core);
-}
-
static inline int xsm_resource_setup_pci(
xsm_default_t def, uint32_t machine_bdf)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -77,8 +77,6 @@ static const struct xsm_ops __initconst_
.get_device_group = xsm_get_device_group,
#endif
- .resource_plug_core = xsm_resource_plug_core,
- .resource_unplug_core = xsm_resource_unplug_core,
.resource_plug_pci = xsm_resource_plug_pci,
.resource_unplug_pci = xsm_resource_unplug_pci,
.resource_setup_pci = xsm_resource_setup_pci,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -56,6 +56,9 @@ static int flask_deassign_dtdevice(struc
#endif
#endif /* CONFIG_HAS_PASSTHROUGH */
+static int flask_resource_plug_core(void);
+static int flask_resource_unplug_core(void);
+
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
@@ -898,9 +901,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_page_offline_op:
-#ifdef CONFIG_X86
- case XEN_SYSCTL_cpu_hotplug:
-#endif
return 0;
case XEN_SYSCTL_readconsole:
@@ -952,6 +952,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_getdomaininfolist:
return flask_getdomaininfo(dom_xen);
+#ifdef CONFIG_X86
+ case XEN_SYSCTL_cpu_hotplug:
+ switch ( op->u.cpu_hotplug.op )
+ {
+ case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
+ return flask_resource_plug_core();
+
+ case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
+ return flask_resource_unplug_core();
+
+ default:
+ return avc_unknown_permission("cpu_hotplug", op->u.cpu_hotplug.op);
+ }
+#endif
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
@@ -1229,12 +1246,12 @@ static int cf_check flask_pci_config_per
}
-static int cf_check flask_resource_plug_core(void)
+static int flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
}
-static int cf_check flask_resource_unplug_core(void)
+static int flask_resource_unplug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
@@ -1955,8 +1972,6 @@ static const struct xsm_ops __initconst_
.iomem_mapping_vpci = flask_iomem_mapping,
.pci_config_permission = flask_pci_config_permission,
- .resource_plug_core = flask_resource_plug_core,
- .resource_unplug_core = flask_resource_unplug_core,
.resource_plug_pci = flask_resource_plug_pci,
.resource_unplug_pci = flask_resource_unplug_pci,
.resource_setup_pci = flask_resource_setup_pci,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .page_offline() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -187,10 +187,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->u.page_offline.end < op->u.page_offline.start )
break;
- ret = xsm_page_offline(XSM_HOOK, op->u.page_offline.cmd);
- if ( ret )
- break;
-
ptr = status = xmalloc_array(uint32_t,
(op->u.page_offline.end -
op->u.page_offline.start + 1));
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -428,12 +428,6 @@ static XSM_INLINE int cf_check xsm_resou
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_page_offline(XSM_DEFAULT_ARG uint32_t cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_hypfs_op(XSM_DEFAULT_VOID)
{
XSM_ASSERT_ACTION(XSM_PRIV);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -129,7 +129,6 @@ struct xsm_ops {
int (*resource_setup_gsi)(int gsi);
int (*resource_setup_misc)(void);
- int (*page_offline)(uint32_t cmd);
int (*hypfs_op)(void);
long (*do_xsm_op)(XEN_GUEST_HANDLE_PARAM(void) op);
@@ -522,11 +521,6 @@ static inline int xsm_resource_setup_mis
return alternative_call(xsm_ops.resource_setup_misc);
}
-static inline int xsm_page_offline(xsm_default_t def, uint32_t cmd)
-{
- return alternative_call(xsm_ops.page_offline, cmd);
-}
-
static inline int xsm_hypfs_op(xsm_default_t def)
{
return alternative_call(xsm_ops.hypfs_op);
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -83,7 +83,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = xsm_resource_setup_gsi,
.resource_setup_misc = xsm_resource_setup_misc,
- .page_offline = xsm_page_offline,
.hypfs_op = xsm_hypfs_op,
.hvm_param = xsm_hvm_param,
.hvm_param_altp2mhvm = xsm_hvm_param_altp2mhvm,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -58,6 +58,7 @@ static int flask_deassign_dtdevice(struc
static int flask_resource_plug_core(void);
static int flask_resource_unplug_core(void);
+static int flask_resource_use_core(void);
static uint32_t domain_sid(const struct domain *dom)
{
@@ -899,10 +900,6 @@ static int cf_check flask_sysctl(const s
{
switch ( op->cmd )
{
- /* These have individual XSM hooks */
- case XEN_SYSCTL_page_offline_op:
- return 0;
-
case XEN_SYSCTL_readconsole:
return domain_has_xen(current->domain,
XEN__READCONSOLE |
@@ -933,6 +930,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_pm_op:
return domain_has_xen(current->domain, XEN__PM_OP);
+ case XEN_SYSCTL_page_offline_op:
+ switch ( op->u.page_offline.cmd )
+ {
+ case sysctl_page_offline:
+ return flask_resource_unplug_core();
+
+ case sysctl_page_online:
+ return flask_resource_plug_core();
+
+ case sysctl_query_page_offline:
+ return flask_resource_use_core();
+
+ default:
+ return avc_unknown_permission("page_offline",
+ op->u.page_offline.cmd);
+ }
+
case XEN_SYSCTL_lockprof_op:
return domain_has_xen(current->domain, XEN__LOCKPROF);
@@ -1324,21 +1338,6 @@ static int cf_check flask_resource_setup
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_RESOURCE, RESOURCE__SETUP, NULL);
}
-static inline int cf_check flask_page_offline(uint32_t cmd)
-{
- switch ( cmd )
- {
- case sysctl_page_offline:
- return flask_resource_unplug_core();
- case sysctl_page_online:
- return flask_resource_plug_core();
- case sysctl_query_page_offline:
- return flask_resource_use_core();
- default:
- return avc_unknown_permission("page_offline", cmd);
- }
-}
-
static inline int cf_check flask_hypfs_op(void)
{
return domain_has_xen(current->domain, XEN__HYPFS_OP);
@@ -1978,7 +1977,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = flask_resource_setup_gsi,
.resource_setup_misc = flask_resource_setup_misc,
- .page_offline = flask_page_offline,
.hypfs_op = flask_hypfs_op,
.hvm_param = flask_hvm_param,
.hvm_param_altp2mhvm = flask_hvm_param_altp2mhvm,
From: Jan Beulich <jbeulich@suse.com>
Subject: platform-op/XSM: move resource-{,un}plug-core checks
Integrate the checking with flask_platform_op(); there never really was a
need to defer these checks, as the sub-op has always been known to the
function. As a positive side effect, permissions are then checked at the
same early point with and without Flask.
This is CVE-2026-62427 / part of XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/platform_hypercall.c
+++ b/xen/arch/x86/platform_hypercall.c
@@ -682,10 +682,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu >= nr_cpu_ids || !cpu_present(cpu) ||
clocksource_is_tsc() )
{
@@ -708,10 +704,6 @@ ret_t do_platform_op(
{
int cpu = op->u.cpu_ol.cpuid;
- ret = xsm_resource_unplug_core(XSM_HOOK);
- if ( ret )
- break;
-
if ( cpu == 0 )
{
ret = -EOPNOTSUPP;
@@ -736,20 +728,12 @@ ret_t do_platform_op(
}
case XENPF_cpu_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = cpu_add(op->u.cpu_add.apic_id,
op->u.cpu_add.acpi_id,
op->u.cpu_add.pxm);
break;
case XENPF_mem_hotadd:
- ret = xsm_resource_plug_core(XSM_HOOK);
- if ( ret )
- break;
-
ret = memory_add(op->u.mem_add.spfn,
op->u.mem_add.epfn,
op->u.mem_add.pxm);
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -1564,12 +1564,13 @@ static int cf_check flask_platform_op(ui
switch ( op )
{
#ifdef CONFIG_X86
- /* These operations have their own XSM hooks */
case XENPF_cpu_online:
- case XENPF_cpu_offline:
case XENPF_cpu_hotadd:
case XENPF_mem_hotadd:
- return 0;
+ return flask_resource_plug_core();
+
+ case XENPF_cpu_offline:
+ return flask_resource_unplug_core();
#endif
case XENPF_settime32:
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: pass full struct xen_sysctl to xsm_sysctl()
Subsequently some sub-ops will want to inspect their sub-sub-ops.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Acked-by: Roger Pau Monné <roger.pau@citrix.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -42,7 +42,7 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->interface_version != XEN_SYSCTL_INTERFACE_VERSION )
return -EACCES;
- ret = xsm_sysctl(XSM_PRIV, op->cmd);
+ ret = xsm_sysctl(XSM_PRIV, op);
if ( ret )
return ret;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -179,7 +179,8 @@ static XSM_INLINE int cf_check xsm_domct
}
}
-static XSM_INLINE int cf_check xsm_sysctl(XSM_DEFAULT_ARG int cmd)
+static XSM_INLINE int cf_check xsm_sysctl(
+ XSM_DEFAULT_ARG const struct xen_sysctl *op)
{
XSM_ASSERT_ACTION(XSM_PRIV);
return xsm_default_action(action, current->domain, NULL);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -59,7 +59,7 @@ struct xsm_ops {
int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
- int (*sysctl)(int cmd);
+ int (*sysctl)(const struct xen_sysctl *op);
int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
@@ -238,9 +238,9 @@ static inline int xsm_domctl(xsm_default
return alternative_call(xsm_ops.domctl, d, op);
}
-static inline int xsm_sysctl(xsm_default_t def, int cmd)
+static inline int xsm_sysctl(xsm_default_t def, const struct xen_sysctl *op)
{
- return alternative_call(xsm_ops.sysctl, cmd);
+ return alternative_call(xsm_ops.sysctl, op);
}
static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -901,9 +901,9 @@ static int cf_check flask_domctl(struct
}
}
-static int cf_check flask_sysctl(int cmd)
+static int cf_check flask_sysctl(const struct xen_sysctl *op)
{
- switch ( cmd )
+ switch ( op->cmd )
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
@@ -975,7 +975,7 @@ static int cf_check flask_sysctl(int cmd
XEN2__COVERAGE_OP, NULL);
default:
- return avc_unknown_permission("sysctl", cmd);
+ return avc_unknown_permission("sysctl", op->cmd);
}
}
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .scheduler_op() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sched/core.c
+++ b/xen/common/sched/core.c
@@ -2087,10 +2087,6 @@ long sched_adjust_global(struct xen_sysc
struct cpupool *pool;
int rc;
- rc = xsm_sysctl_scheduler_op(XSM_HOOK, op->cmd);
- if ( rc )
- return rc;
-
if ( (op->cmd != XEN_SYSCTL_SCHEDOP_putinfo) &&
(op->cmd != XEN_SYSCTL_SCHEDOP_getinfo) )
return -EINVAL;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -141,12 +141,6 @@ static XSM_INLINE int cf_check xsm_getdo
return xsm_default_action(action, current->domain, d);
}
-static XSM_INLINE int cf_check xsm_sysctl_scheduler_op(XSM_DEFAULT_ARG int cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_set_target(
XSM_DEFAULT_ARG struct domain *d, struct domain *e)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -56,7 +56,6 @@ struct xsm_ops {
struct xen_domctl_getdomaininfo *info);
int (*domain_create)(struct domain *d, uint32_t ssidref);
int (*getdomaininfo)(struct domain *d);
- int (*sysctl_scheduler_op)(int op);
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
@@ -221,11 +220,6 @@ static inline int xsm_getdomaininfo(xsm_
return alternative_call(xsm_ops.getdomaininfo, d);
}
-static inline int xsm_sysctl_scheduler_op(xsm_default_t def, int cmd)
-{
- return alternative_call(xsm_ops.sysctl_scheduler_op, cmd);
-}
-
static inline int xsm_set_target(
xsm_default_t def, struct domain *d, struct domain *e)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -18,7 +18,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = xsm_security_domaininfo,
.domain_create = xsm_domain_create,
.getdomaininfo = xsm_getdomaininfo,
- .sysctl_scheduler_op = xsm_sysctl_scheduler_op,
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -638,7 +638,7 @@ static int flask_domctl_scheduler_op(str
}
}
-static int cf_check flask_sysctl_scheduler_op(int op)
+static int flask_sysctl_scheduler_op(unsigned int op)
{
switch ( op )
{
@@ -909,7 +909,6 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
- case XEN_SYSCTL_scheduler_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
@@ -945,6 +944,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_cpupool_op:
return domain_has_xen(current->domain, XEN__CPUPOOL_OP);
+ case XEN_SYSCTL_scheduler_op:
+ return flask_sysctl_scheduler_op(op->u.scheduler_op.cmd);
+
case XEN_SYSCTL_physinfo:
case XEN_SYSCTL_cputopoinfo:
case XEN_SYSCTL_numainfo:
@@ -1916,7 +1918,6 @@ static const struct xsm_ops __initconst_
.security_domaininfo = flask_security_domaininfo,
.domain_create = flask_domain_create,
.getdomaininfo = flask_getdomaininfo,
- .sysctl_scheduler_op = flask_sysctl_scheduler_op,
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/Flask: add preliminary check for XEN_SYSCTL_getdomaininfolist
To shield the sysctl lock from abuse by unauthorized domains, follow what
5154fdda1124 ("domctl: protect locking for get_domain_state") did: Check
for permission to issue the operation against DOM_XEN in flask_sysctl().
The finer-grained xsm_getdomaininfo() later in the handling of the sub-op
remains unaltered.
In the in-tree policy respective permission therefore needs granting.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/tools/flask/policy/modules/xen.if
+++ b/tools/flask/policy/modules/xen.if
@@ -90,6 +90,7 @@ define(`create_domain_build_label', `
# manage_domain(priv, target)
# Allow managing a running domain
define(`manage_domain', `
+ allow $1 domxen_t:domain getdomaininfo;
allow $1 $2:domain { getdomaininfo getvcpuinfo getaffinity
getaddrsize pause unpause trigger shutdown destroy
setaffinity setdomainmaxmem getscheduler resume
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -907,7 +907,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
- case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
@@ -954,6 +953,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_get_cpu_policy:
return domain_has_xen(current->domain, XEN__PHYSINFO);
+ case XEN_SYSCTL_getdomaininfolist:
+ return flask_getdomaininfo(dom_xen);
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .readconsole() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -59,10 +59,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
switch ( op->cmd )
{
case XEN_SYSCTL_readconsole:
- ret = xsm_readconsole(XSM_HOOK, op->u.readconsole.clear);
- if ( ret )
- break;
-
ret = read_console_ring(&op->u.readconsole);
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -180,12 +180,6 @@ static XSM_INLINE int cf_check xsm_sysct
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_readconsole(XSM_DEFAULT_ARG uint32_t clear)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_alloc_security_domain(struct domain *d)
{
return 0;
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -59,7 +59,6 @@ struct xsm_ops {
int (*set_target)(struct domain *d, struct domain *e);
int (*domctl)(struct domain *d, struct xen_domctl *op);
int (*sysctl)(const struct xen_sysctl *op);
- int (*readconsole)(uint32_t clear);
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
int (*evtchn_interdomain)(struct domain *d1, struct evtchn *chn1,
@@ -237,11 +236,6 @@ static inline int xsm_sysctl(xsm_default
return alternative_call(xsm_ops.sysctl, op);
}
-static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
-{
- return alternative_call(xsm_ops.readconsole, clear);
-}
-
static inline int xsm_evtchn_unbound(
xsm_default_t def, struct domain *d1, struct evtchn *chn, domid_t id2)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -21,7 +21,6 @@ static const struct xsm_ops __initconst_
.set_target = xsm_set_target,
.domctl = xsm_domctl,
.sysctl = xsm_sysctl,
- .readconsole = xsm_readconsole,
.evtchn_unbound = xsm_evtchn_unbound,
.evtchn_interdomain = xsm_evtchn_interdomain,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -906,13 +906,18 @@ static int cf_check flask_sysctl(const s
switch ( op->cmd )
{
/* These have individual XSM hooks */
- case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
return 0;
+ case XEN_SYSCTL_readconsole:
+ return domain_has_xen(current->domain,
+ XEN__READCONSOLE |
+ (op->u.readconsole.clear ? XEN__CLEARCONSOLE
+ : 0));
+
case XEN_SYSCTL_tbuf_op:
return domain_has_xen(current->domain, XEN__TBUFCONTROL);
@@ -983,16 +988,6 @@ static int cf_check flask_sysctl(const s
}
}
-static int cf_check flask_readconsole(uint32_t clear)
-{
- uint32_t perms = XEN__READCONSOLE;
-
- if ( clear )
- perms |= XEN__CLEARCONSOLE;
-
- return domain_has_xen(current->domain, perms);
-}
-
static inline uint32_t resource_to_perm(uint8_t access)
{
if ( access )
@@ -1923,7 +1918,6 @@ static const struct xsm_ops __initconst_
.set_target = flask_set_target,
.domctl = flask_domctl,
.sysctl = flask_sysctl,
- .readconsole = flask_readconsole,
.evtchn_unbound = flask_evtchn_unbound,
.evtchn_interdomain = flask_evtchn_interdomain,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .resource_{,un}plug_core() hooks
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask. Note that these were x86-
only, i.e. some dead/unreachable code gets eliminated for (in particular)
Arm.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/sysctl.c
+++ b/xen/arch/x86/sysctl.c
@@ -117,20 +117,17 @@ long arch_do_sysctl(
{
unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
unsigned int op = sysctl->u.cpu_hotplug.op;
- bool plug;
long (*fn)(void *data);
void *hcpu;
switch ( op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
- plug = true;
fn = cpu_up_helper;
hcpu = _p(cpu);
break;
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
- plug = false;
fn = cpu_down_helper;
hcpu = _p(cpu);
break;
@@ -150,9 +147,8 @@ long arch_do_sysctl(
if ( CONFIG_NR_CPUS <= 1 )
/* Mimic behavior of smt_up_down_helper(). */
return 0;
- plug = op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE;
fn = smt_up_down_helper;
- hcpu = _p(plug);
+ hcpu = _p(op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE);
break;
default:
@@ -161,10 +157,6 @@ long arch_do_sysctl(
}
if ( !ret )
- ret = plug ? xsm_resource_plug_core(XSM_HOOK)
- : xsm_resource_unplug_core(XSM_HOOK);
-
- if ( !ret )
ret = continue_hypercall_on_cpu(0, fn, hcpu);
}
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -396,18 +396,6 @@ static XSM_INLINE int cf_check xsm_get_d
}
#endif /* HAS_PASSTHROUGH && HAS_PCI */
-static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
-static XSM_INLINE int cf_check xsm_resource_unplug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_resource_plug_pci(
XSM_DEFAULT_ARG uint32_t machine_bdf)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -122,8 +122,6 @@ struct xsm_ops {
int (*get_device_group)(uint32_t machine_bdf);
#endif
- int (*resource_plug_core)(void);
- int (*resource_unplug_core)(void);
int (*resource_plug_pci)(uint32_t machine_bdf);
int (*resource_unplug_pci)(uint32_t machine_bdf);
int (*resource_setup_pci)(uint32_t machine_bdf);
@@ -507,16 +505,6 @@ static inline int xsm_resource_unplug_pc
return alternative_call(xsm_ops.resource_unplug_pci, machine_bdf);
}
-static inline int xsm_resource_plug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_plug_core);
-}
-
-static inline int xsm_resource_unplug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_unplug_core);
-}
-
static inline int xsm_resource_setup_pci(
xsm_default_t def, uint32_t machine_bdf)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -77,8 +77,6 @@ static const struct xsm_ops __initconst_
.get_device_group = xsm_get_device_group,
#endif
- .resource_plug_core = xsm_resource_plug_core,
- .resource_unplug_core = xsm_resource_unplug_core,
.resource_plug_pci = xsm_resource_plug_pci,
.resource_unplug_pci = xsm_resource_unplug_pci,
.resource_setup_pci = xsm_resource_setup_pci,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -56,6 +56,9 @@ static int flask_deassign_dtdevice(struc
#endif
#endif /* CONFIG_HAS_PASSTHROUGH */
+static int flask_resource_plug_core(void);
+static int flask_resource_unplug_core(void);
+
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
@@ -907,9 +910,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_page_offline_op:
-#ifdef CONFIG_X86
- case XEN_SYSCTL_cpu_hotplug:
-#endif
return 0;
case XEN_SYSCTL_readconsole:
@@ -961,6 +961,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_getdomaininfolist:
return flask_getdomaininfo(dom_xen);
+#ifdef CONFIG_X86
+ case XEN_SYSCTL_cpu_hotplug:
+ switch ( op->u.cpu_hotplug.op )
+ {
+ case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
+ return flask_resource_plug_core();
+
+ case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
+ return flask_resource_unplug_core();
+
+ default:
+ return avc_unknown_permission("cpu_hotplug", op->u.cpu_hotplug.op);
+ }
+#endif
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
@@ -1238,12 +1255,12 @@ static int cf_check flask_pci_config_per
}
-static int cf_check flask_resource_plug_core(void)
+static int flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
}
-static int cf_check flask_resource_unplug_core(void)
+static int flask_resource_unplug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
@@ -1969,8 +1986,6 @@ static const struct xsm_ops __initconst_
.iomem_mapping_vpci = flask_iomem_mapping,
.pci_config_permission = flask_pci_config_permission,
- .resource_plug_core = flask_resource_plug_core,
- .resource_unplug_core = flask_resource_unplug_core,
.resource_plug_pci = flask_resource_plug_pci,
.resource_unplug_pci = flask_resource_unplug_pci,
.resource_setup_pci = flask_resource_setup_pci,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .page_offline() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -187,10 +187,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->u.page_offline.end < op->u.page_offline.start )
break;
- ret = xsm_page_offline(XSM_HOOK, op->u.page_offline.cmd);
- if ( ret )
- break;
-
ptr = status = xmalloc_array(uint32_t,
(op->u.page_offline.end -
op->u.page_offline.start + 1));
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -429,12 +429,6 @@ static XSM_INLINE int cf_check xsm_resou
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_page_offline(XSM_DEFAULT_ARG uint32_t cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_hypfs_op(XSM_DEFAULT_VOID)
{
XSM_ASSERT_ACTION(XSM_PRIV);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -128,7 +128,6 @@ struct xsm_ops {
int (*resource_setup_gsi)(int gsi);
int (*resource_setup_misc)(void);
- int (*page_offline)(uint32_t cmd);
int (*hypfs_op)(void);
long (*do_xsm_op)(XEN_GUEST_HANDLE_PARAM(void) op);
@@ -521,11 +520,6 @@ static inline int xsm_resource_setup_mis
return alternative_call(xsm_ops.resource_setup_misc);
}
-static inline int xsm_page_offline(xsm_default_t def, uint32_t cmd)
-{
- return alternative_call(xsm_ops.page_offline, cmd);
-}
-
static inline int xsm_hypfs_op(xsm_default_t def)
{
return alternative_call(xsm_ops.hypfs_op);
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -83,7 +83,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = xsm_resource_setup_gsi,
.resource_setup_misc = xsm_resource_setup_misc,
- .page_offline = xsm_page_offline,
.hypfs_op = xsm_hypfs_op,
.hvm_param = xsm_hvm_param,
.hvm_param_altp2mhvm = xsm_hvm_param_altp2mhvm,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -58,6 +58,7 @@ static int flask_deassign_dtdevice(struc
static int flask_resource_plug_core(void);
static int flask_resource_unplug_core(void);
+static int flask_resource_use_core(void);
static uint32_t domain_sid(const struct domain *dom)
{
@@ -908,10 +909,6 @@ static int cf_check flask_sysctl(const s
{
switch ( op->cmd )
{
- /* These have individual XSM hooks */
- case XEN_SYSCTL_page_offline_op:
- return 0;
-
case XEN_SYSCTL_readconsole:
return domain_has_xen(current->domain,
XEN__READCONSOLE |
@@ -942,6 +939,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_pm_op:
return domain_has_xen(current->domain, XEN__PM_OP);
+ case XEN_SYSCTL_page_offline_op:
+ switch ( op->u.page_offline.cmd )
+ {
+ case sysctl_page_offline:
+ return flask_resource_unplug_core();
+
+ case sysctl_page_online:
+ return flask_resource_plug_core();
+
+ case sysctl_query_page_offline:
+ return flask_resource_use_core();
+
+ default:
+ return avc_unknown_permission("page_offline",
+ op->u.page_offline.cmd);
+ }
+
case XEN_SYSCTL_lockprof_op:
return domain_has_xen(current->domain, XEN__LOCKPROF);
@@ -1333,21 +1347,6 @@ static int cf_check flask_resource_setup
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_RESOURCE, RESOURCE__SETUP, NULL);
}
-static inline int cf_check flask_page_offline(uint32_t cmd)
-{
- switch ( cmd )
- {
- case sysctl_page_offline:
- return flask_resource_unplug_core();
- case sysctl_page_online:
- return flask_resource_plug_core();
- case sysctl_query_page_offline:
- return flask_resource_use_core();
- default:
- return avc_unknown_permission("page_offline", cmd);
- }
-}
-
static inline int cf_check flask_hypfs_op(void)
{
return domain_has_xen(current->domain, XEN__HYPFS_OP);
@@ -1992,7 +1991,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = flask_resource_setup_gsi,
.resource_setup_misc = flask_resource_setup_misc,
- .page_offline = flask_page_offline,
.hypfs_op = flask_hypfs_op,
.hvm_param = flask_hvm_param,
.hvm_param_altp2mhvm = flask_hvm_param_altp2mhvm,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/Flask: add preliminary check for XEN_SYSCTL_getdomaininfolist
To shield the sysctl lock from abuse by unauthorized domains, follow what
5154fdda1124 ("domctl: protect locking for get_domain_state") did: Check
for permission to issue the operation against DOM_XEN in flask_sysctl().
The finer-grained xsm_getdomaininfo() later in the handling of the sub-op
remains unaltered.
In the in-tree policy respective permission therefore needs granting.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-by: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/tools/flask/policy/modules/xen.if
+++ b/tools/flask/policy/modules/xen.if
@@ -150,6 +150,7 @@ define(`create_domain_build_label', `
# manage_domain(priv, target)
# Allow managing a running domain
define(`manage_domain', `
+ allow $1 domxen_t:domain getdomaininfo;
allow $1 $2:domain {
getdomaininfo
getvcpuinfo
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -878,7 +878,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_readconsole:
- case XEN_SYSCTL_getdomaininfolist:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
@@ -925,6 +924,9 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_get_cpu_policy:
return domain_has_xen(current->domain, XEN__PHYSINFO);
+ case XEN_SYSCTL_getdomaininfolist:
+ return flask_getdomaininfo(dom_xen);
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .readconsole() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -59,10 +59,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
switch ( op->cmd )
{
case XEN_SYSCTL_readconsole:
- ret = xsm_readconsole(XSM_HOOK, op->u.readconsole.clear);
- if ( ret )
- break;
-
ret = read_console_ring(&op->u.readconsole);
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -181,12 +181,6 @@ static XSM_INLINE int cf_check xsm_sysct
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_readconsole(XSM_DEFAULT_ARG uint32_t clear)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_alloc_security_domain(struct domain *d)
{
return 0;
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -60,7 +60,6 @@ struct xsm_ops {
int (*domctl)(struct domain *d, struct xen_domctl *op);
#ifdef CONFIG_SYSCTL
int (*sysctl)(const struct xen_sysctl *op);
- int (*readconsole)(uint32_t clear);
#endif
int (*evtchn_unbound)(struct domain *d, struct evtchn *chn, domid_t id2);
@@ -245,11 +244,6 @@ static inline int xsm_sysctl(xsm_default
{
return alternative_call(xsm_ops.sysctl, op);
}
-
-static inline int xsm_readconsole(xsm_default_t def, uint32_t clear)
-{
- return alternative_call(xsm_ops.readconsole, clear);
-}
#endif
static inline int xsm_evtchn_unbound(
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -22,7 +22,6 @@ static const struct xsm_ops __initconst_
.domctl = xsm_domctl,
#ifdef CONFIG_SYSCTL
.sysctl = xsm_sysctl,
- .readconsole = xsm_readconsole,
#endif
.evtchn_unbound = xsm_evtchn_unbound,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -877,13 +877,18 @@ static int cf_check flask_sysctl(const s
switch ( op->cmd )
{
/* These have individual XSM hooks */
- case XEN_SYSCTL_readconsole:
case XEN_SYSCTL_page_offline_op:
#ifdef CONFIG_X86
case XEN_SYSCTL_cpu_hotplug:
#endif
return 0;
+ case XEN_SYSCTL_readconsole:
+ return domain_has_xen(current->domain,
+ XEN__READCONSOLE |
+ (op->u.readconsole.clear ? XEN__CLEARCONSOLE
+ : 0));
+
case XEN_SYSCTL_tbuf_op:
return domain_has_xen(current->domain, XEN__TBUFCONTROL);
@@ -949,16 +954,6 @@ static int cf_check flask_sysctl(const s
return avc_unknown_permission("sysctl", op->cmd);
}
}
-
-static int cf_check flask_readconsole(uint32_t clear)
-{
- uint32_t perms = XEN__READCONSOLE;
-
- if ( clear )
- perms |= XEN__CLEARCONSOLE;
-
- return domain_has_xen(current->domain, perms);
-}
#endif /* CONFIG_SYSCTL */
static inline uint32_t resource_to_perm(uint8_t access)
@@ -1903,7 +1898,6 @@ static const struct xsm_ops __initconst_
.domctl = flask_domctl,
#ifdef CONFIG_SYSCTL
.sysctl = flask_sysctl,
- .readconsole = flask_readconsole,
#endif
.evtchn_unbound = flask_evtchn_unbound,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .resource_{,un}plug_core() hooks
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask. Note that these were x86-
only, i.e. some dead/unreachable code gets eliminated for (in particular)
Arm.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/arch/x86/sysctl.c
+++ b/xen/arch/x86/sysctl.c
@@ -117,20 +117,17 @@ long arch_do_sysctl(
{
unsigned int cpu = sysctl->u.cpu_hotplug.cpu;
unsigned int op = sysctl->u.cpu_hotplug.op;
- bool plug;
long (*fn)(void *data);
void *hcpu;
switch ( op )
{
case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
- plug = true;
fn = cpu_up_helper;
hcpu = _p(cpu);
break;
case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
- plug = false;
fn = cpu_down_helper;
hcpu = _p(cpu);
break;
@@ -150,9 +147,8 @@ long arch_do_sysctl(
if ( CONFIG_NR_CPUS <= 1 )
/* Mimic behavior of smt_up_down_helper(). */
return 0;
- plug = op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE;
fn = smt_up_down_helper;
- hcpu = _p(plug);
+ hcpu = _p(op == XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE);
break;
default:
@@ -161,10 +157,6 @@ long arch_do_sysctl(
}
if ( !ret )
- ret = plug ? xsm_resource_plug_core(XSM_HOOK)
- : xsm_resource_unplug_core(XSM_HOOK);
-
- if ( !ret )
ret = continue_hypercall_on_cpu(0, fn, hcpu);
}
break;
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -390,18 +390,6 @@ static XSM_INLINE int cf_check xsm_get_d
}
#endif /* HAS_PASSTHROUGH && HAS_PCI */
-static XSM_INLINE int cf_check xsm_resource_plug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
-static XSM_INLINE int cf_check xsm_resource_unplug_core(XSM_DEFAULT_VOID)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_resource_plug_pci(
XSM_DEFAULT_ARG uint32_t machine_bdf)
{
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -122,8 +122,6 @@ struct xsm_ops {
int (*get_device_group)(uint32_t machine_bdf);
#endif
- int (*resource_plug_core)(void);
- int (*resource_unplug_core)(void);
int (*resource_plug_pci)(uint32_t machine_bdf);
int (*resource_unplug_pci)(uint32_t machine_bdf);
int (*resource_setup_pci)(uint32_t machine_bdf);
@@ -512,16 +510,6 @@ static inline int xsm_resource_unplug_pc
return alternative_call(xsm_ops.resource_unplug_pci, machine_bdf);
}
-static inline int xsm_resource_plug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_plug_core);
-}
-
-static inline int xsm_resource_unplug_core(xsm_default_t def)
-{
- return alternative_call(xsm_ops.resource_unplug_core);
-}
-
static inline int xsm_resource_setup_pci(
xsm_default_t def, uint32_t machine_bdf)
{
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -77,8 +77,6 @@ static const struct xsm_ops __initconst_
.get_device_group = xsm_get_device_group,
#endif
- .resource_plug_core = xsm_resource_plug_core,
- .resource_unplug_core = xsm_resource_unplug_core,
.resource_plug_pci = xsm_resource_plug_pci,
.resource_unplug_pci = xsm_resource_unplug_pci,
.resource_setup_pci = xsm_resource_setup_pci,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -55,6 +55,11 @@ static int flask_deassign_dtdevice(struc
#endif
#endif /* CONFIG_HAS_PASSTHROUGH */
+#if defined(CONFIG_SYSCTL) || defined(CONFIG_X86)
+static int flask_resource_plug_core(void);
+static int flask_resource_unplug_core(void);
+#endif
+
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
@@ -878,9 +883,6 @@ static int cf_check flask_sysctl(const s
{
/* These have individual XSM hooks */
case XEN_SYSCTL_page_offline_op:
-#ifdef CONFIG_X86
- case XEN_SYSCTL_cpu_hotplug:
-#endif
return 0;
case XEN_SYSCTL_readconsole:
@@ -932,6 +934,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_getdomaininfolist:
return flask_getdomaininfo(dom_xen);
+#ifdef CONFIG_X86
+ case XEN_SYSCTL_cpu_hotplug:
+ switch ( op->u.cpu_hotplug.op )
+ {
+ case XEN_SYSCTL_CPU_HOTPLUG_ONLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_ENABLE:
+ return flask_resource_plug_core();
+
+ case XEN_SYSCTL_CPU_HOTPLUG_OFFLINE:
+ case XEN_SYSCTL_CPU_HOTPLUG_SMT_DISABLE:
+ return flask_resource_unplug_core();
+
+ default:
+ return avc_unknown_permission("cpu_hotplug", op->u.cpu_hotplug.op);
+ }
+#endif
+
case XEN_SYSCTL_psr_cmt_op:
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_XEN2,
XEN2__PSR_CMT_OP, NULL);
@@ -1207,12 +1226,12 @@ static int cf_check flask_pci_config_per
}
#if defined(CONFIG_SYSCTL) || defined(CONFIG_X86)
-static int cf_check flask_resource_plug_core(void)
+static int flask_resource_plug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__PLUG, NULL);
}
-static int cf_check flask_resource_unplug_core(void)
+static int flask_resource_unplug_core(void)
{
return avc_current_has_perm(SECINITSID_DOMXEN, SECCLASS_RESOURCE, RESOURCE__UNPLUG, NULL);
}
@@ -1948,8 +1967,6 @@ static const struct xsm_ops __initconst_
.iomem_mapping_vpci = flask_iomem_mapping,
.pci_config_permission = flask_pci_config_permission,
- .resource_plug_core = flask_resource_plug_core,
- .resource_unplug_core = flask_resource_unplug_core,
.resource_plug_pci = flask_resource_plug_pci,
.resource_unplug_pci = flask_resource_unplug_pci,
.resource_setup_pci = flask_resource_setup_pci,
From: Jan Beulich <jbeulich@suse.com>
Subject: sysctl/XSM: drop .page_offline() hook
Integrate the checking with xsm_sysctl(), now that it has the full op
struct passed. As a positive side effect, permissions are then checked at
the same early point with and without Flask.
This is part of CVE-2026-62426 / XSA-499.
Signed-off-by: Jan Beulich <jbeulich@suse.com>
Reviewed-by: Andrew Cooper <andrew.cooper3@citrix.com>
Acked-By: Daniel P. Smith <dpsmith@apertussolutions.com>
--- a/xen/common/sysctl.c
+++ b/xen/common/sysctl.c
@@ -189,10 +189,6 @@ long do_sysctl(XEN_GUEST_HANDLE_PARAM(xe
if ( op->u.page_offline.end < op->u.page_offline.start )
break;
- ret = xsm_page_offline(XSM_HOOK, op->u.page_offline.cmd);
- if ( ret )
- break;
-
ptr = status = xmalloc_array(uint32_t,
(op->u.page_offline.end -
op->u.page_offline.start + 1));
--- a/xen/include/xsm/dummy.h
+++ b/xen/include/xsm/dummy.h
@@ -423,12 +423,6 @@ static XSM_INLINE int cf_check xsm_resou
return xsm_default_action(action, current->domain, NULL);
}
-static XSM_INLINE int cf_check xsm_page_offline(XSM_DEFAULT_ARG uint32_t cmd)
-{
- XSM_ASSERT_ACTION(XSM_HOOK);
- return xsm_default_action(action, current->domain, NULL);
-}
-
static XSM_INLINE int cf_check xsm_hypfs_op(XSM_DEFAULT_VOID)
{
XSM_ASSERT_ACTION(XSM_PRIV);
--- a/xen/include/xsm/xsm.h
+++ b/xen/include/xsm/xsm.h
@@ -128,9 +128,6 @@ struct xsm_ops {
int (*resource_setup_gsi)(int gsi);
int (*resource_setup_misc)(void);
-#ifdef CONFIG_SYSCTL
- int (*page_offline)(uint32_t cmd);
-#endif
int (*hypfs_op)(void);
long (*do_xsm_op)(XEN_GUEST_HANDLE_PARAM(void) op);
@@ -526,13 +523,6 @@ static inline int xsm_resource_setup_mis
return alternative_call(xsm_ops.resource_setup_misc);
}
-#ifdef CONFIG_SYSCTL
-static inline int xsm_page_offline(xsm_default_t def, uint32_t cmd)
-{
- return alternative_call(xsm_ops.page_offline, cmd);
-}
-#endif
-
static inline int xsm_hypfs_op(xsm_default_t def)
{
return alternative_call(xsm_ops.hypfs_op);
--- a/xen/xsm/dummy.c
+++ b/xen/xsm/dummy.c
@@ -83,9 +83,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = xsm_resource_setup_gsi,
.resource_setup_misc = xsm_resource_setup_misc,
-#ifdef CONFIG_SYSCTL
- .page_offline = xsm_page_offline,
-#endif
.hypfs_op = xsm_hypfs_op,
.hvm_param = xsm_hvm_param,
.hvm_param_altp2mhvm = xsm_hvm_param_altp2mhvm,
--- a/xen/xsm/flask/hooks.c
+++ b/xen/xsm/flask/hooks.c
@@ -60,6 +60,10 @@ static int flask_resource_plug_core(void
static int flask_resource_unplug_core(void);
#endif
+#ifdef CONFIG_SYSCTL
+static int flask_resource_use_core(void);
+#endif
+
static uint32_t domain_sid(const struct domain *dom)
{
struct domain_security_struct *dsec = dom->ssid;
@@ -881,10 +885,6 @@ static int cf_check flask_sysctl(const s
{
switch ( op->cmd )
{
- /* These have individual XSM hooks */
- case XEN_SYSCTL_page_offline_op:
- return 0;
-
case XEN_SYSCTL_readconsole:
return domain_has_xen(current->domain,
XEN__READCONSOLE |
@@ -915,6 +915,23 @@ static int cf_check flask_sysctl(const s
case XEN_SYSCTL_pm_op:
return domain_has_xen(current->domain, XEN__PM_OP);
+ case XEN_SYSCTL_page_offline_op:
+ switch ( op->u.page_offline.cmd )
+ {
+ case sysctl_page_offline:
+ return flask_resource_unplug_core();
+
+ case sysctl_page_online:
+ return flask_resource_plug_core();
+
+ case sysctl_query_page_offline:
+ return flask_resource_use_core();
+
+ default:
+ return avc_unknown_permission("page_offline",
+ op->u.page_offline.cmd);
+ }
+
case XEN_SYSCTL_lockprof_op:
return domain_has_xen(current->domain, XEN__LOCKPROF);
@@ -1307,23 +1324,6 @@ static int cf_check flask_resource_setup
return avc_current_has_perm(SECINITSID_XEN, SECCLASS_RESOURCE, RESOURCE__SETUP, NULL);
}
-#ifdef CONFIG_SYSCTL
-static inline int cf_check flask_page_offline(uint32_t cmd)
-{
- switch ( cmd )
- {
- case sysctl_page_offline:
- return flask_resource_unplug_core();
- case sysctl_page_online:
- return flask_resource_plug_core();
- case sysctl_query_page_offline:
- return flask_resource_use_core();
- default:
- return avc_unknown_permission("page_offline", cmd);
- }
-}
-#endif /* CONFIG_SYSCTL */
-
static inline int cf_check flask_hypfs_op(void)
{
return domain_has_xen(current->domain, XEN__HYPFS_OP);
@@ -1973,9 +1973,6 @@ static const struct xsm_ops __initconst_
.resource_setup_gsi = flask_resource_setup_gsi,
.resource_setup_misc = flask_resource_setup_misc,
-#ifdef CONFIG_SYSCTL
- .page_offline = flask_page_offline,
-#endif
.hypfs_op = flask_hypfs_op,
.hvm_param = flask_hvm_param,
.hvm_param_altp2mhvm = flask_hvm_param_altp2mhvm,
© 2016 - 2026 Red Hat, Inc.