Skip to content

Commit

Permalink
x86/xen: fix out of bounds access to the event channel masks on resume
Browse files Browse the repository at this point in the history
When resuming from migration or suspension all regular event channels ports are
reset to the INVALID_EVTCHN value, and drivers should re-initialize them
according to the new value provided by the other end of the connection.

However, the driver would first attempt to unbind the event channel handler
before attempting to bind it using the newly provided port.  This unbind uses
the stale event channel port that has been set to INVALID_EVTCHN for some
operations (notably as a result of the handler removal the interrupt subsystem
ends up calling disable intr and source PIC hooks).

This was fine when INVALID_EVTCHN was 0, as then the operation would just
result in pointless setting of the 0 bit in the different event channel related
control arrays (evtchn_{pending,mask} for example).  However with the change to
define INVALID_EVTCHN as ~0 the write is no longer pointless, and we end up
triggering a page-fault, or corrupting random data that happens to be mapped at
the array position + ~0 bits.

In hindsight the change of INVALID_EVTCHN from 0 to ~0 was way more risky than
initially assessed, and I believe has end up resulting in more fragile code for
no real benefit.

Fix the disable intr and source wrappers to check whether the event channel is
valid before attempting to use it.

Also introduce some extra KASSERTs in several array accesses in order to avoid
out of bounds accesses if INVALID_EVTCHN ever reaches those functions.

Fixes: 1797ff9 ('xen/intr: cleanup event channel number use')
MFC after: 1 week
Sponsored by: Cloud Software Group
Reviewed by: markj
Differential revision: https://reviews.freebsd.org/D43928
  • Loading branch information
Roger Pau Monné authored and Roger Pau Monné committed Feb 22, 2024
1 parent bad90cb commit 4ece799
Show file tree
Hide file tree
Showing 2 changed files with 11 additions and 4 deletions.
8 changes: 6 additions & 2 deletions sys/dev/xen/bus/xen_intr.c
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,7 @@ evtchn_cpu_mask_port(u_int cpu, evtchn_port_t port)
struct xen_intr_pcpu_data *pcpu;

pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu);
KASSERT(is_valid_evtchn(port), ("Invalid event channel port"));
xen_clear_bit(port, pcpu->evtchn_enabled);
}

Expand All @@ -188,6 +189,7 @@ evtchn_cpu_unmask_port(u_int cpu, evtchn_port_t port)
struct xen_intr_pcpu_data *pcpu;

pcpu = DPCPU_ID_PTR(cpu, xen_intr_pcpu);
KASSERT(is_valid_evtchn(port), ("Invalid event channel port"));
xen_set_bit(port, pcpu->evtchn_enabled);
}

Expand Down Expand Up @@ -619,7 +621,8 @@ void
xen_intr_disable_intr(struct xenisrc *isrc)
{

evtchn_mask_port(isrc->xi_port);
if (__predict_true(is_valid_evtchn(isrc->xi_port)))
evtchn_mask_port(isrc->xi_port);
}

/**
Expand Down Expand Up @@ -706,7 +709,8 @@ xen_intr_disable_source(struct xenisrc *isrc)
* unmasked by the generic interrupt code. The event channel
* device will unmask them when needed.
*/
isrc->xi_masked = !!evtchn_test_and_set_mask(isrc->xi_port);
if (__predict_true(is_valid_evtchn(isrc->xi_port)))
isrc->xi_masked = !!evtchn_test_and_set_mask(isrc->xi_port);
}

/*
Expand Down
7 changes: 5 additions & 2 deletions sys/xen/evtchn/evtchnvar.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,11 @@
#include <contrib/xen/event_channel.h>

/* Macros for accessing event channel values */
#define EVTCHN_PTR(type, port) \
(HYPERVISOR_shared_info->evtchn_##type + ((port) / __LONG_BIT))
#define EVTCHN_PTR(type, port) ({ \
KASSERT(port < nitems(HYPERVISOR_shared_info->evtchn_##type) * \
sizeof(xen_ulong_t) * 8, ("Invalid event channel port")); \
(HYPERVISOR_shared_info->evtchn_##type + ((port) / __LONG_BIT));\
})
#define EVTCHN_BIT(port) ((port) & (__LONG_BIT - 1))
#define EVTCHN_MASK(port) (1UL << EVTCHN_BIT(port))

Expand Down

0 comments on commit 4ece799

Please sign in to comment.