freebsd-src/sys/xen/evtchn/evtchnvar.h
Roger Pau Monné 4ece79968e x86/xen: fix out of bounds access to the event channel masks on resume
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: 1797ff9627 ('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
2024-02-22 11:08:03 +01:00

102 lines
3.2 KiB
C

/******************************************************************************
* evtchn.h
*
* Data structures and definitions private to the FreeBSD implementation
* of the Xen event channel API.
*
* Copyright (c) 2004, K A Fraser
* Copyright (c) 2012, Spectra Logic Corporation
* Copyright © 2022, Elliott Mitchell
*
* This file may be distributed separately from the Linux kernel, or
* incorporated into other software packages, subject to the following license:
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this source file (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use, copy, modify,
* merge, publish, distribute, sublicense, and/or sell copies of the Software,
* and to permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
* IN THE SOFTWARE.
*/
#ifndef __XEN_EVTCHN_EVTCHNVAR_H__
#define __XEN_EVTCHN_EVTCHNVAR_H__
#include <xen/hypervisor.h>
#include <contrib/xen/event_channel.h>
/* Macros for accessing event channel values */
#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))
/**
* Disable signal delivery for an event channel port, returning its
* previous mask state.
*
* \param port The event channel port to query and mask.
*
* \returns 1 if event delivery was previously disabled. Otherwise 0.
*/
static inline int
evtchn_test_and_set_mask(evtchn_port_t port)
{
return (atomic_testandset_xen_ulong(EVTCHN_PTR(mask, port),
EVTCHN_BIT(port)));
}
/**
* Clear any pending event for the given event channel port.
*
* \param port The event channel port to clear.
*/
static inline void
evtchn_clear_port(evtchn_port_t port)
{
atomic_clear_xen_ulong(EVTCHN_PTR(pending, port), EVTCHN_MASK(port));
}
/**
* Disable signal delivery for an event channel port.
*
* \param port The event channel port to mask.
*/
static inline void
evtchn_mask_port(evtchn_port_t port)
{
atomic_set_xen_ulong(EVTCHN_PTR(mask, port), EVTCHN_MASK(port));
}
/**
* Enable signal delivery for an event channel port.
*
* \param port The event channel port to enable.
*/
static inline void
evtchn_unmask_port(evtchn_port_t port)
{
evtchn_unmask_t op = { .port = port };
HYPERVISOR_event_channel_op(EVTCHNOP_unmask, &op);
}
#endif /* __XEN_EVTCHN_EVTCHNVAR_H__ */