⬢ DragonFlyBSD Kernel Audit
DF-5516 / ub5516attachstall_harness.c
← back to finding ↓ download raw
/*
 * DF-5516 KLD harness -- production-code demonstration that
 * ubser_attach()'s GET_NUMSER vendor request pins the per-bus USB
 * explore (hub event) thread for the full USB_DEFAULT_TIMEOUT = 5000 ms
 * (usbdi.h:87) at every insertion of an evil BWCT-branded adapter.
 *
 * Evidence class: DF-5488/DF-5505 verbatim extraction.  build.sh
 * sed-extracts ubser.c:242-262 (the "get number of serials" block of
 * ubser_attach) UNMODIFIED from the guest's md5-gated /usr/src tree
 * into ub5516_block.c; this module includes that fragment inside the
 * simulated attach.  The only modeled pieces are the EP0 sink
 * (usbd_do_request_flags interposed) and device_printf.  The fragment's
 * `goto detach' (ubser.c:257) binds to the local `detach:' label, which
 * stands in for the production label at ubser.c:296 (ubser_detach +
 * return ENXIO).
 *
 * REACHABILITY: ubser_probe (ubser.c:206-221) matches on
 *   - usb_get_manufacturer(uaa->device) == "BWCT" (device string
 *     descriptor -- attacker-controlled firmware),
 *   - bInterfaceClass == 0xff, bInterfaceSubClass == 0x00
 * so an evil adapter needs only a 4-byte manufacturer string and a
 * vendor-specific interface descriptor to be handed to ubser_attach.
 * GET_NUMSER is the FIRST thing attach does with the device
 * (ubser.c:242-251), synchronously, on the attaching thread -- the
 * per-bus USB explore thread that is enumerating every other device on
 * that bus (family mechanism, DF-5359/DF-5407/DF-5488/DF-5505).
 *
 * PHASE A silent evil device (NAKs the data stage until timeout):
 *          the verbatim block sleeps the full 5000 ms in the "WXFER"
 *          wait, gets USB_ERR_TIMEOUT, prints the verbatim error via
 *          the verbatim usbd_errstr() call, and takes the verbatim
 *          `goto detach' abort.  EXPECT explore-thread occupancy
 *          ~= 5000 ms, attach ABORT.
 * PHASE B slow-answer evil device (answers at 4900 ms, numser=4):
 *          the request SUCCEEDS -- no error is visible anywhere in the
 *          driver -- and attach CONTINUES (verbatim "found 4 serials").
 *          EXPECT occupancy ~= 4900 ms, attach CONTINUES (honest worst
 *          case: an evil device need not fail anything).
 * PHASE C  fixed build only (fix.diff: USB_DEFAULT_TIMEOUT -> 1000 at
 *          ubser.c:251): EXPECT occupancy ~= 1000 ms fail-fast.
 *          See run.fixed.log.
 */
#include <sys/param.h>
#include <sys/types.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/lock.h>
#include <sys/time.h>
#include <machine/stdarg.h>

#include <bus/u4b/usb.h>
#include <bus/u4b/usbdi.h>

MODULE_DEPEND(ub5516attach, usb, 1, 1, 1);
MODULE_DEPEND(ub5516attach, ucom, 1, 1, 1);

/*
 * Modeled EP0 sink.  Signature must match usbdi.h:508-510 exactly
 * (the verbatim fragment calls it through that prototype).
 */
static int ub_evil_mode;	/* 0 = silent, 1 = slow-answer */
static char ub_wchan[16];

usb_error_t
usbd_do_request_flags(struct usb_device *udev, struct lock *lock,
    struct usb_device_request *req, void *data, uint16_t flags,
    uint16_t *actlen, usb_timeout_t timeout)
{
	int ms = (int)timeout;

	kprintf("UB5516: EP0 bRequest=0x%02x wLength=%d (%d ms timeout): "
	    "device %s\n", req->bRequest, UGETW(req->wLength), ms,
	    ub_evil_mode ? "answers at just-under-timeout" :
	    "silent -- NAKs until timeout");

	if (ub_evil_mode) {
		tsleep(&ub_wchan, 0, "WXFER", USB_MS_TO_TICKS(ms - 100));
		if (data != NULL)
			*(uint8_t *)data = 4;	/* claims 4 serial ports */
		return (USB_ERR_NORMAL_COMPLETION);
	}
	/* production passes lock == NULL here (ubser.c:249-251), so the
	 * model plain-sleeps like the core requester does without a lock */
	tsleep(&ub_wchan, 0, "WXFER", USB_MS_TO_TICKS(ms));
	return (USB_ERR_TIMEOUT);
}

/* device_printf model (the verbatim block calls it at ubser.c:254/262) */
int
device_printf(device_t dev, const char *fmt, ...)
{
	__va_list ap;

	kprintf("UB5516 DEV: ");
	__va_start(ap, fmt);
	kvprintf(fmt, ap);
	__va_end(ap);
	return (0);
}

static int64_t
ub_ms(struct timeval *t0, struct timeval *t1)
{
	return ((int64_t)(t1->tv_sec - t0->tv_sec) * 1000 +
	    (t1->tv_usec - t0->tv_usec) / 1000);
}

/*
 * The verbatim block needs `sc', `uaa', `req', `error', `dev' and the
 * VENDOR_GET_NUMSER / UT_READ_VENDOR_INTERFACE / USETW / UBSER_UNIT_MAX
 * definitions -- all provided by including the production file
 * (registration neutered), exactly like the DF-5447 rig.
 */
#undef DRIVER_MODULE
#undef DRIVER_MODULE_ORDERED
#undef MODULE_DEPEND
#undef MODULE_VERSION
#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg)
#define DRIVER_MODULE_ORDERED(name, busname, driver, devclass, evh, arg, order)
#define MODULE_DEPEND(module, mdepend, vmin, vpref, vmax)
#define MODULE_VERSION(module, version)

#include "../ubser.c"

static struct ubser_softc ub_sc;
static struct usb_attach_arg ub_uaa;
static int ub_devident;
static device_t ub_dev = (device_t)&ub_devident;	/* printf is modeled */

static void
ub_touch_statics(void)
{

	if (ubser_devclass != NULL)
		kprintf("UB5516: unexpected devclass %p\n", ubser_devclass);
	kprintf("UB5516: production driver \"%s\" compiled into harness "
	    "(registration neutered)\n", ubser_driver.name);
}

static void
ub_simulate_attach(char tag, int evil_mode)
{
	struct timeval t0, t1;
	struct usb_device_request req;
	int error;

	ub_evil_mode = evil_mode;
	memset(&ub_sc, 0, sizeof(ub_sc));
	memset(&ub_uaa, 0, sizeof(ub_uaa));
	ub_uaa.info.bIfaceNum = 0;
	ub_uaa.device = (void *)&ub_wchan;	/* model sink ignores it */

	{
		/* local aliases exactly as ubser_attach declares them
		 * (ubser.c:226-230) */
		struct usb_attach_arg *uaa = &ub_uaa;
		struct ubser_softc *sc = &ub_sc;
		device_t dev = ub_dev;

		sc->sc_iface_no = uaa->info.bIfaceNum;

		microtime(&t0);
		kprintf("UB5516[%c]: --- insertion: ubser_attach runs on the "
		    "per-bus USB explore thread, GET_NUMSER first "
		    "(ubser.c:242-251) --- (t=0)\n", tag);

/* THE VERBATIM PRODUCTION BLOCK, extracted by build.sh from
 * ubser.c:242-262 of the md5-gated guest tree.  Its `goto detach'
 * binds to the local label below, which stands in for the production
 * label at ubser.c:296-298 (ubser_detach(dev); return ENXIO). */
#include "ub5516_block.c"

		/* success fall-through: production continues into
		 * usbd_transfer_setup (ubser.c:264) */
		microtime(&t1);
		kprintf("UB5516[%c]: explore-thread occupancy %jd ms; attach "
		    "CONTINUES past the block (usbd_transfer_setup next, "
		    "ubser.c:264)\n", tag, (intmax_t)ub_ms(&t0, &t1));
		goto out;

detach:
		microtime(&t1);
		kprintf("UB5516[%c]: explore-thread occupancy %jd ms; attach "
		    "ABORT via verbatim `goto detach' (production target "
		    "ubser.c:296-298: ubser_detach + ENXIO)\n",
		    tag, (intmax_t)ub_ms(&t0, &t1));
out:
		;
	}
}

static int
ub_modevent(module_t mod, int type, void *data)
{
	int error = 0;

	switch (type) {
	case MOD_LOAD:
		kprintf("UB5516: DF-5516 ubser attach-pin harness "
		    "(USB_DEFAULT_TIMEOUT=%d ms, usbdi.h:87)\n",
		    USB_DEFAULT_TIMEOUT);
		ub_touch_statics();
		ub_simulate_attach('A', 0);	/* silent evil: full 5 s */
		ub_simulate_attach('B', 1);	/* slow answer: 4.9 s, success */
		kprintf("UB5516: harness complete (phase C = fixed build "
		    "rerun, see run.fixed.log)\n");
		break;
	case MOD_UNLOAD:
		break;
	default:
		error = EOPNOTSUPP;
		break;
	}
	return (error);
}

static moduledata_t ub_mod = { "ub5516attach", ub_modevent, 0 };
DECLARE_MODULE(ub5516attach, ub_mod, SI_SUB_DRIVERS, SI_ORDER_MIDDLE);