Subject: sysctl/XSM: drop .resource_{,un}plug_core() hooks
From: Jan Beulich <[email-masked]>

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 <[email-masked]>
Reviewed-by: Andrew Cooper <[email-masked]>
Acked-By: Daniel P. Smith <[email-masked]>

--- 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,