⬢ DragonFlyBSD Kernel Audit
DF-4957 / ciphysink.c
← back to finding ↓ download raw
/*
 * DF-4957 sink demonstration module (ciphy variant of the DF-4930 atphy
 * proof, same kernel sink, adapted for the Cicada/Vitesse PHY driver).
 *
 * Executes the exact terminal sequence of the ciphy no-media attach chain:
 *
 *   ciphy_attach (ciphy.c:142-144) skips mii_phy_add_media ("no media
 *   present" -- BMSR_MEDIAMASK and EXTSR_MEDIAMASK both 0), still calls
 *   MIIBUS_MEDIAINIT (ciphy.c:149)
 *     -> miibus_mediainit (mii.c:229-246): walks an EMPTY ifm_list,
 *        media stays 0, calls ifmedia_set(&mii->mii_media, 0)
 *     -> ifmedia_set (net/if_media.c): ifmedia_match() on an empty list
 *        returns NULL -> kprintf + panic("ifmedia_set")
 *
 * DIVERGENCE FROM THE atphy FIX, important: ciphy always sets
 * MIIF_NOISOLATE (ciphy.c:132), and mii_phy_add_media()'s IFM_NONE
 * fallback entry is gated on MIIF_NOISOLATE being CLEAR
 * (mii_physubr.c:230-232) -- so the atphy-shaped one-liner ("just call
 * mii_phy_add_media unconditionally") adds NOTHING for ciphy and the
 * panic remains.  The ciphy fix.diff therefore adds an explicit
 * IFM_NONE fallback entry in the no-media branch.
 *
 * hw.ciphysink_fixcheck=1 replays the sequence WITH the explicit
 * fallback entry (the fixed ciphy path): must NOT panic.
 * hw.ciphysink_fire=1   replays the stock ciphy path: panics the kernel.
 *
 * Only the PHY hardware state (a CS8201/VSC8601 whose ID registers match
 * the ciphys[] table while BMSR/EXTSR report no media bits) is not
 * emulatable on this guest; the kernel-side sink is executed verbatim.
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/conf.h>
#include <sys/sysctl.h>
#include <sys/systm.h>
#include <net/if.h>
#include <net/if_media.h>

static struct ifmedia ifm;

static int
ciphysink_fire(SYSCTL_HANDLER_ARGS)
{
	int v = 0, error;
	struct ifmedia_entry *m;
	int media = 0;

	error = sysctl_handle_opaque(oidp, &v, sizeof(v), req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	kprintf("ciphysink: ifmedia_init (empty list)\n");
	ifmedia_init(&ifm, 0, NULL, NULL);

	/* mii.c:234-238 verbatim */
	for (m = LIST_FIRST(&ifm.ifm_list); m != NULL;
	     m = LIST_NEXT(m, ifm_list)) {
		media = m->ifm_media;
		if (media == (IFM_ETHER | IFM_AUTO))
			break;
	}
	kprintf("ciphysink: list walk done, media=0x%x; calling ifmedia_set (mii.c:244)\n",
	    media);
	ifmedia_set(&ifm, media);	/* expected: panic("ifmedia_set") */

	kprintf("ciphysink: NOT REACHED\n");
	return (0);
}

static int
ciphysink_fixcheck(SYSCTL_HANDLER_ARGS)
{
	struct ifmedia ifm2;
	int v = 0, error;

	error = sysctl_handle_opaque(oidp, &v, sizeof(v), req);
	if (error != 0 || req->newptr == NULL)
		return (error);

	kprintf("ciphysink-fix: ifmedia_init + explicit IFM_NONE entry (the fixed ciphy path)\n");
	ifmedia_init(&ifm2, 0, NULL, NULL);
	ifmedia_add(&ifm2,
	    IFM_MAKEWORD(IFM_ETHER, IFM_NONE, 0, 0), 0, NULL);
	kprintf("ciphysink-fix: calling ifmedia_set(IFM_ETHER|IFM_NONE) -- must NOT panic\n");
	ifmedia_set(&ifm2, IFM_ETHER | IFM_NONE);
	kprintf("ciphysink-fix: survived; ifm_cur=%p (non-NULL keeps ciphy_service safe)\n",
	    ifm2.ifm_cur);
	ifmedia_removeall(&ifm2);
	return (0);
}

static int
ciphysink_modevent(module_t mod, int type, void *arg)
{
	static struct sysctl_ctx_list ctx;

	switch (type) {
	case MOD_LOAD:
		sysctl_ctx_init(&ctx);
		SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
		    "ciphysink_fire", CTLTYPE_INT | CTLFLAG_RW,
		    0, 0, ciphysink_fire, "I", "fire the empty-list ifmedia_set");
		SYSCTL_ADD_PROC(NULL, SYSCTL_STATIC_CHILDREN(_hw), OID_AUTO,
		    "ciphysink_fixcheck", CTLTYPE_INT | CTLFLAG_RW,
		    0, 0, ciphysink_fixcheck, "I", "replay with the fixed fallback entry");
		kprintf("ciphysink: loaded; hw.ciphysink_fixcheck=1 then hw.ciphysink_fire=1\n");
		return (0);
	case MOD_UNLOAD:
		sysctl_ctx_free(&ctx);
		return (0);
	}
	return (0);
}
DEV_MODULE(ciphysink, ciphysink_modevent, NULL);