⬢ DragonFlyBSD Kernel Audit
DF-5464 / icu_msiabi_livelock.c
← back to finding ↓ download raw
/*
 * DF-5464 crasher #2 (livelock): msi_alloc(count = 0).
 *
 * With count == 0 the loop at icu_abi.c:473
 *     for (i = msi_start; i < IDT_HWI_VECTORS; i += count)
 * never advances (i += 0) and the body 'continue's forever
 * (icu_irqmaps[0][0].im_type is ICU_IMT_LEGACY != UNUSED on the first
 * iteration and i never changes) -- while lwkt_gettoken(&icu_irqmap_tok)
 * (icu_abi.c:463) is held.  The loading thread spins forever; every
 * later ICU MSI path (msi_alloc/msi_release/msi_map) blocks on the token.
 *
 * Expected: kldload never returns; 'timeout' on the caller side expires
 * (exit status 124) while the guest stays alive (other CPUs unaffected).
 *
 * On an INVARIANTS kernel this panics at KASSERT(count > 0)
 * (icu_abi.c:460) instead.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/machintr.h>

extern struct machintr_abi MachIntrABI_ICU;

static int zone[8];

static int
icu_live_modevent(module_t mod __unused, int type,
    void *data __unused)
{
	int rc;

	switch (type) {
	case MOD_LOAD:
		kprintf("icu_msiabi_livelock: calling ICU msi_alloc(count=0, "
		    "cpu0): expect infinite loop at icu_abi.c:473 holding "
		    "icu_irqmap_tok -- kldload will not return\n");
		rc = MachIntrABI_ICU.msi_alloc(zone, 0, 0);
		kprintf("icu_msiabi_livelock: returned rc=%d (unexpected on "
		    "a production kernel)\n", rc);
		return 0;
	case MOD_UNLOAD:
		return 0;
	default:
		return EOPNOTSUPP;
	}
}

static moduledata_t icu_msiabi_livelock_mod = {
	"icu_msiabi_livelock", icu_live_modevent, NULL
};
DECLARE_MODULE(icu_msiabi_livelock, icu_msiabi_livelock_mod,
    SI_SUB_DRIVERS, SI_ORDER_MIDDLE);
MODULE_VERSION(icu_msiabi_livelock, 1);