[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[xen master] xen: introduce CONFIG_HAS_SHARED_INFO for archs without a shared page



commit 31fb44f8c02019f3a44e7ef6df0fbe0f2bce2ca3
Author:     Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
AuthorDate: Wed Sep 9 17:07:19 2026 +0200
Commit:     Jan Beulich <jbeulich@xxxxxxxx>
CommitDate: Mon Sep 21 10:05:20 2026 +0200

    xen: introduce CONFIG_HAS_SHARED_INFO for archs without a shared page
    
    On architectures that run guests in dom0less mode without the PV ABI
    (currently RISC-V), no shared_info page is allocated and d->shared_info
    remains NULL throughout the domain lifetime.  Several places in common
    code access d->shared_info through the shared_info() macro or directly,
    causing UBSAN null-pointer errors on such architectures.
    
    Rather than adding runtime NULL guards that are logically unreachable
    on x86 and Arm (where shared_info is always allocated), introduce a new
    Kconfig symbol CONFIG_HAS_SHARED_INFO selected by x86 and Arm.
    
    On !HAS_SHARED_INFO the shared_info() macro expands to a dereference
    of shared_info_absent, an extern pointer that is declared but
    intentionally never defined.  Any use of shared_info() that is not
    dead-code-eliminated will therefore cause a link-time failure, making
    missed guards impossible to overlook.
    
    The 2L event-channel ops call shared_info() and must not be compiled on
    architectures without a shared_info page, so event_2l.o is gated on
    CONFIG_HAS_SHARED_INFO.  On such architectures evtchn_init() installs the
    FIFO ops as a placeholder instead, so that a later guest opt-in to the
    FIFO ABI via EVTCHNOP_init_control has no special-casing to do; if FIFO
    support itself is also unavailable (!CONFIG_EVTCHN_FIFO), a dedicated
    no-op evtchn_port_ops_none table is installed instead, so that
    d->evtchn_port_ops is never NULL.  evtchn_fifo_word_from_port() is
    guarded against uninitialised d->evtchn_fifo so the FIFO ops are safe
    before evtchn_fifo_init_control() is called by the guest.
    
    With CONFIG_HAS_SHARED_INFO=n all vCPUs fall back to the global
    dummy_vcpu_info, so writes through vcpu_info() could leak data between
    vCPUs. Reviewing the write paths in common code: the write in
    map_guest_area() stores the constant ~0 so nothing serious would happen
    if it were leaked; the event_2l.c paths are not compiled on
    !HAS_SHARED_INFO, as event_2l.o is gated on CONFIG_HAS_SHARED_INFO; the
    write in vcpu_info_populate() targets the new mapping buffer, not
    dummy_vcpu_info.
    
    Outside common code, the remaining writes are x86 PV-specific, for which
    CONFIG_HAS_SHARED_INFO=y. No code changes are needed.
    
    Finally, struct domain's shared_info field itself is gated on
    CONFIG_HAS_SHARED_INFO, as it would otherwise be a permanently NULL
    pointer: every user of it is either arch code for an architecture that
    selects HAS_SHARED_INFO, or common code already guarded by the same
    Kconfig symbol.
    
    Signed-off-by: Oleksii Kurochko <oleksii.kurochko@xxxxxxxxx>
    Reviewed-by: Jan Beulich <jbeulich@xxxxxxxx>
    Acked-by: Michal Orzel <michal.orzel@xxxxxxx> #Arm
---
 xen/arch/arm/Kconfig       |  1 +
 xen/arch/x86/Kconfig       |  1 +
 xen/common/Kconfig         |  3 +++
 xen/common/Makefile        |  2 +-
 xen/common/domain.c        |  6 ++---
 xen/common/domctl.c        |  4 ++++
 xen/common/event_channel.c | 55 +++++++++++++++++++++++++++++++++++++++++++---
 xen/common/event_channel.h |  6 +++++
 xen/common/event_fifo.c    | 18 ++++++++++++++-
 xen/common/time.c          |  2 ++
 xen/include/xen/event.h    |  2 +-
 xen/include/xen/sched.h    |  2 ++
 xen/include/xen/shared.h   |  6 +++++
 xen/include/xen/time.h     |  5 +++++
 14 files changed, 104 insertions(+), 9 deletions(-)

diff --git a/xen/arch/arm/Kconfig b/xen/arch/arm/Kconfig
index 843a43897e..d748404e82 100644
--- a/xen/arch/arm/Kconfig
+++ b/xen/arch/arm/Kconfig
@@ -20,6 +20,7 @@ config ARM
        select HAS_DEVICE_TREE_DISCOVERY
        select HAS_DOM0LESS
        select HAS_GRANT_CACHE_FLUSH if GRANT_TABLE
+       select HAS_SHARED_INFO
        select HAS_STACK_PROTECTOR
        select HAS_STATIC_MEMORY
        select HAS_UBSAN
diff --git a/xen/arch/x86/Kconfig b/xen/arch/x86/Kconfig
index 3ce0774b8d..e5535ac484 100644
--- a/xen/arch/x86/Kconfig
+++ b/xen/arch/x86/Kconfig
@@ -29,6 +29,7 @@ config X86
        select HAS_PCI_MSI
        select HAS_PIRQ
        select HAS_SCHED_GRANULARITY
+       select HAS_SHARED_INFO
        imply HAS_SOFT_RESET
        select HAS_UBSAN
        select HAS_VMAP
diff --git a/xen/common/Kconfig b/xen/common/Kconfig
index da80fdba84..5b289e444f 100644
--- a/xen/common/Kconfig
+++ b/xen/common/Kconfig
@@ -158,6 +158,9 @@ config HAS_PMAP
 config HAS_SCHED_GRANULARITY
        bool
 
+config HAS_SHARED_INFO
+       bool
+
 config HAS_STATIC_MEMORY
        bool
 
diff --git a/xen/common/Makefile b/xen/common/Makefile
index 6018e25614..f69d47d189 100644
--- a/xen/common/Makefile
+++ b/xen/common/Makefile
@@ -12,7 +12,7 @@ obj-$(CONFIG_DEVICE_TREE_PARSE) += device-tree/
 obj-$(CONFIG_IOREQ_SERVER) += dm.o
 obj-y += domain.o
 obj-y += domid.o
-obj-y += event_2l.o
+obj-$(CONFIG_HAS_SHARED_INFO) += event_2l.o
 obj-y += event_channel.o
 obj-$(CONFIG_EVTCHN_FIFO) += event_fifo.o
 obj-$(CONFIG_GRANT_TABLE) += grant_table.o
diff --git a/xen/common/domain.c b/xen/common/domain.c
index e16f1ac383..6b52713518 100644
--- a/xen/common/domain.c
+++ b/xen/common/domain.c
@@ -316,9 +316,9 @@ void vcpu_info_reset(struct vcpu *v)
     struct domain *d = v->domain;
 
     v->vcpu_info_area.map =
-        ((v->vcpu_id < XEN_LEGACY_MAX_VCPUS)
-         ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id])
-         : &dummy_vcpu_info);
+        IS_ENABLED(CONFIG_HAS_SHARED_INFO) && v->vcpu_id < XEN_LEGACY_MAX_VCPUS
+        ? (vcpu_info_t *)&shared_info(d, vcpu_info[v->vcpu_id])
+        : &dummy_vcpu_info;
 }
 
 static struct domain *alloc_domain_struct(void)
diff --git a/xen/common/domctl.c b/xen/common/domctl.c
index a6210db4fb..83405a766a 100644
--- a/xen/common/domctl.c
+++ b/xen/common/domctl.c
@@ -102,9 +102,13 @@ void getdomaininfo(struct domain *d, struct 
xen_domctl_getdomaininfo *info)
 #ifdef CONFIG_MEM_PAGING
     info->paged_pages       = atomic_read(&d->paged_pages);
 #endif
+#ifdef CONFIG_HAS_SHARED_INFO
     info->shared_info_frame =
         gfn_x(mfn_to_gfn(d, _mfn(virt_to_mfn(d->shared_info))));
     BUG_ON(SHARED_M2P(info->shared_info_frame));
+#else
+    info->shared_info_frame = ~0;
+#endif
 
     info->cpupool = cpupool_get_id(d);
 
diff --git a/xen/common/event_channel.c b/xen/common/event_channel.c
index a7f9cc5fe0..bd03e094d0 100644
--- a/xen/common/event_channel.c
+++ b/xen/common/event_channel.c
@@ -40,6 +40,54 @@
 
 #define consumer_is_xen(e) (!!(e)->xen_consumer)
 
+#if !defined(CONFIG_HAS_SHARED_INFO) && !defined(CONFIG_EVTCHN_FIFO)
+/*
+ * Placeholder ops for domains with neither a shared_info page nor a FIFO
+ * control block. Such a domain has no ABI to record event state in, so these
+ * are reachable whenever an event is delivered to (or queried on) one of its
+ * ports; they just discard/no-op it. They exist to keep d->evtchn_port_ops
+ * non-NULL.
+ */
+static void cf_check evtchn_none_set_pending(
+    struct vcpu *v, struct evtchn *evtchn) {}
+static void cf_check evtchn_none_noop(
+    struct domain *d, struct evtchn *evtchn) {}
+static bool cf_check evtchn_none_false(
+    const struct domain *d, const struct evtchn *evtchn) { return false; }
+static void cf_check evtchn_none_print_state(
+    struct domain *d, const struct evtchn *evtchn) {}
+
+static const struct evtchn_port_ops evtchn_port_ops_none = {
+    .set_pending   = evtchn_none_set_pending,
+    .clear_pending = evtchn_none_noop,
+    .unmask        = evtchn_none_noop,
+    .is_pending    = evtchn_none_false,
+    .is_masked     = evtchn_none_false,
+    .print_state   = evtchn_none_print_state,
+};
+
+static void evtchn_none_init(struct domain *d)
+{
+    d->evtchn_port_ops = &evtchn_port_ops_none;
+}
+#else /* CONFIG_HAS_SHARED_INFO || CONFIG_EVTCHN_FIFO */
+/*
+ * Declaration only; the call in evtchn_preinit() is DCE'd unless both
+ * configs are off.
+ */
+void evtchn_none_init(struct domain *d);
+#endif /* !CONFIG_HAS_SHARED_INFO && !CONFIG_EVTCHN_FIFO */
+
+static void evtchn_preinit(struct domain *d)
+{
+    if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) )
+        evtchn_2l_init(d);
+    else if ( IS_ENABLED(CONFIG_EVTCHN_FIFO) )
+        evtchn_fifo_init_ops(d);
+    else
+        evtchn_none_init(d);
+}
+
 /*
  * Lock an event channel exclusively. This is allowed only when the channel is
  * free or unbound either when taking or when releasing the lock, as any
@@ -1324,9 +1372,9 @@ int evtchn_reset(struct domain *d, bool resuming)
         rc = -EAGAIN;
     else if ( d->evtchn_fifo )
     {
-        /* Switching back to 2-level ABI. */
+        /* Switching back to the default ABI. */
         evtchn_fifo_destroy(d);
-        evtchn_2l_init(d);
+        evtchn_preinit(d);
     }
 
     write_unlock(&d->event_lock);
@@ -1625,7 +1673,8 @@ void evtchn_check_pollers(struct domain *d, unsigned int 
port)
 
 int evtchn_init(struct domain *d, unsigned int max_port)
 {
-    evtchn_2l_init(d);
+    evtchn_preinit(d);
+
     d->max_evtchn_port = min_t(unsigned int, max_port, INT_MAX);
 
     d->evtchn = alloc_evtchn_bucket(d, 0);
diff --git a/xen/common/event_channel.h b/xen/common/event_channel.h
index dc94a43cc2..423c4ee77b 100644
--- a/xen/common/event_channel.h
+++ b/xen/common/event_channel.h
@@ -70,6 +70,12 @@ static inline void evtchn_fifo_destroy(struct domain *d)
 }
 #endif /* CONFIG_EVTCHN_FIFO */
 
+/*
+ * Declaration only when !CONFIG_EVTCHN_FIFO; the call in evtchn_preinit() is
+ * DCE'd in that case.
+ */
+void evtchn_fifo_init_ops(struct domain *d);
+
 #endif /* EVENT_CHANNEL_H */
 
 /*
diff --git a/xen/common/event_fifo.c b/xen/common/event_fifo.c
index 611bf8a788..e4225b1b66 100644
--- a/xen/common/event_fifo.c
+++ b/xen/common/event_fifo.c
@@ -62,6 +62,9 @@ static inline event_word_t *evtchn_fifo_word_from_port(const 
struct domain *d,
      */
     smp_rmb();
 
+    if ( unlikely(!d->evtchn_fifo) )
+        return NULL;
+
     if ( unlikely(port >= d->evtchn_fifo->num_evtchns) )
         return NULL;
 
@@ -419,6 +422,18 @@ static const struct evtchn_port_ops evtchn_port_ops_fifo =
     .print_state   = evtchn_fifo_print_state,
 };
 
+/*
+ * evtchn_fifo_init_ops()'s only call site is the
+ * IS_ENABLED(CONFIG_EVTCHN_FIFO) branch of evtchn_preinit(), which is never
+ * reached on HAS_SHARED_INFO=y builds because of DCE.
+ */
+#ifndef CONFIG_HAS_SHARED_INFO
+void evtchn_fifo_init_ops(struct domain *d)
+{
+    d->evtchn_port_ops = &evtchn_port_ops_fifo;
+}
+#endif
+
 static int map_guest_page(struct domain *d, uint64_t gfn, void **virt)
 {
     struct page_info *p;
@@ -561,7 +576,8 @@ static void setup_ports(struct domain *d, unsigned int 
prev_evtchns)
 
         evtchn = evtchn_from_port(d, port);
 
-        if ( guest_test_bit(d, port, &shared_info(d, evtchn_pending)) )
+        if ( IS_ENABLED(CONFIG_HAS_SHARED_INFO) &&
+             guest_test_bit(d, port, &shared_info(d, evtchn_pending)) )
             evtchn->pending = true;
 
         evtchn_fifo_set_priority(d, evtchn, EVTCHN_FIFO_PRIORITY_DEFAULT);
diff --git a/xen/common/time.c b/xen/common/time.c
index 0ddf65448d..58d2b54a29 100644
--- a/xen/common/time.c
+++ b/xen/common/time.c
@@ -98,6 +98,7 @@ struct tm gmtime(unsigned long t)
     return tbuf;
 }
 
+#ifdef CONFIG_HAS_SHARED_INFO
 void update_domain_wallclock_time(struct domain *d)
 {
     uint32_t *wc_version;
@@ -126,6 +127,7 @@ void update_domain_wallclock_time(struct domain *d)
 
     spin_unlock(&wc_lock);
 }
+#endif /* CONFIG_HAS_SHARED_INFO */
 
 /* Set clock to <secs,usecs> after 00:00:00 UTC, 1 January, 1970. */
 void do_settime(u64 secs, unsigned int nsecs, u64 system_time_base)
diff --git a/xen/include/xen/event.h b/xen/include/xen/event.h
index 930190054c..595dedf079 100644
--- a/xen/include/xen/event.h
+++ b/xen/include/xen/event.h
@@ -211,7 +211,7 @@ static bool evtchn_usable(const struct evtchn *evtchn)
 
 void evtchn_check_pollers(struct domain *d, unsigned int port);
 
-/* Close all event channels and reset to 2-level ABI. */
+/* Close all event channels and reset to the default ABI. */
 int evtchn_reset(struct domain *d, bool resuming);
 
 /*
diff --git a/xen/include/xen/sched.h b/xen/include/xen/sched.h
index e352e2b38e..73c54794ac 100644
--- a/xen/include/xen/sched.h
+++ b/xen/include/xen/sched.h
@@ -404,7 +404,9 @@ struct domain
 
     struct vcpu    **vcpu;
 
+#ifdef CONFIG_HAS_SHARED_INFO
     shared_info_t   *shared_info;     /* shared data area */
+#endif
 
     rcu_read_lock_t  rcu_lock;
 
diff --git a/xen/include/xen/shared.h b/xen/include/xen/shared.h
index 5b71342cab..1589e8793f 100644
--- a/xen/include/xen/shared.h
+++ b/xen/include/xen/shared.h
@@ -43,7 +43,13 @@ typedef struct vcpu_info vcpu_info_t;
 
 extern vcpu_info_t dummy_vcpu_info;
 
+#ifdef CONFIG_HAS_SHARED_INFO
 #define shared_info(d, field)      __shared_info(d, (d)->shared_info, field)
+#else
+extern struct shared_info *shared_info_absent;
+#define shared_info(d, field) (((void)(d), shared_info_absent)->field)
+#endif /* CONFIG_HAS_SHARED_INFO */
+
 #define vcpu_info(v, field)        \
         __vcpu_info(v, (vcpu_info_t *)(v)->vcpu_info_area.map, field)
 
diff --git a/xen/include/xen/time.h b/xen/include/xen/time.h
index 4db24617a9..09da150c2a 100644
--- a/xen/include/xen/time.h
+++ b/xen/include/xen/time.h
@@ -72,7 +72,12 @@ extern bool NOW_good;
 #define version_update_begin(v) (((v) + 1) | 1)
 #define version_update_end(v)   ((v) + 1)
 extern void update_vcpu_system_time(struct vcpu *v);
+
+#ifdef CONFIG_HAS_SHARED_INFO
 extern void update_domain_wallclock_time(struct domain *d);
+#else
+static inline void update_domain_wallclock_time(struct domain *d) {}
+#endif
 
 extern void do_settime(
     u64 secs, unsigned int nsecs, u64 system_time_base);
--
generated by git-patchbot for /home/xen/git/xen.git#master



 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.