⬢ DragonFlyBSD Kernel Audit
DF-2327 / ppbharness3.c
← back to finding ↓ download raw
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
/*
 * ppbharness3 -- KLD harness for sys/bus/ppbus/ppbconf.c
 * (DF-2327 / DF-2328 / DF-2329 evidence rig)
 *
 * Rig family: "KLD-harness-with-production-code" (no parallel port in
 * QEMU) -- same pattern as the DF-5510/DF-5513 ppbharness2 rig, extended:
 *   - compiles the *unmodified* production sources ppbconf.c, ppb_1284.c,
 *     ppb_base.c out of /usr/src/sys/bus/ppbus;
 *   - a fake "ppc" chipset on nexus serves a fully scripted IEEE1284
 *     peer (crafted device-ID strings);
 *   - tests are ARMED VIA SYSCTL (hw.ppbx3.test) so the panic cases run
 *     one per boot under explicit control:
 *       test=1  DF-2327  ID "MFG;MFG" (semicolon->NUL, no colon)
 *                search_token(...,":")==NULL -> NULL+1 -> kprintf %s @0x1
 *                -> Fatal trap 12 (ppbconf.c:242)
 *       test=2  DF-2328  256-byte NUL-free ID with trailing "REV" and
 *                no colon: search_token(UNKNOWN_LENGTH,":") walks past
 *                str[255] into str[256] (uninitialized; read_id bzeros
 *                only PPB_PnP_STRING_SIZE bytes) and beyond into stack
 *                (ppbconf.c:187-188,253-259) -> OOB read; on this guest
 *                it finds no ':' in the crossed bytes -> NULL+1 trap
 *                (same symptom as 2327 but reached via the missing
 *                NUL termination)
 *       test=3  DF-2329  lost-wakeup race: owner releases in the window
 *                between requester's crit_exit() (ppbconf.c:467) and
 *                tsleep() (:470/:474); wakeup is delivered before the
 *                sleep -> requester sleeps forever on a FREE bus
 *                (PPB_WAIT|PPB_NOINTR = uninterruptible)
 *       test=4  control: well-formed ID "MFG:X;MDL:Y;VER:1;CLS:PRINTER;"
 *                -> clean probe, no trap
 */
#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/bus.h>
#include <sys/systm.h>
#include <sys/types.h>
#include <sys/kthread.h>
#include <sys/thread.h>
#include <sys/time.h>
#include <sys/sysctl.h>

#include <machine/clock.h>

#include <bus/ppbus/ppbconf.h>
#include <bus/ppbus/ppb_1284.h>

#include "ppbus_if.h"

/* ------------------------------------------------------------------ */
/* fake ppc: per-instance scripted IEEE1284 peer                       */
/* ------------------------------------------------------------------ */

struct fakeppc_softc {
	int		phase;
#define PH_IDLE	0
#define PH_NEG	1
#define PH_READ	2
	u_char		last_wctr;
	u_char		last_wdtr;

	char		stream[256];
	int		stream_len;
	int		byte_i;
	int		nib_sel;
};

static __inline u_char
hx_enc(int nib)
{
	return ((nib & 1) ? nFAULT : 0) | ((nib & 2) ? SELECT : 0) |
	       ((nib & 4) ? PERROR : 0) | ((nib & 8) ? 0 : nBUSY);
}

static u_char
hx_rstr(struct fakeppc_softc *sc)
{
	switch (sc->phase) {
	case PH_NEG:
		if (sc->last_wctr == 0x04)
			return (0xD8);
		return (0x38);
	case PH_READ:
		if (sc->byte_i < sc->stream_len) {
			int nib = (sc->nib_sel == 0) ?
			    (sc->stream[sc->byte_i] & 0x0f) :
			    ((sc->stream[sc->byte_i] >> 4) & 0x0f);
			if (sc->last_wctr == 0x06)
				return ((u_char)(hx_enc(nib) & ~nACK));
			if (sc->last_wctr == 0x04)
				return ((u_char)((hx_enc(nib) | nACK) & ~nFAULT));
			if (sc->last_wctr == 0x0C)
				return (0x88);
			if (sc->last_wctr == 0x0E)
				return (0xD8);
			return (0x48);
		}
		if (sc->last_wctr == 0x0C)
			return (0x88);
		if (sc->last_wctr == 0x0E)
			return (0xD8);
		return (0x48);
	default:
		return (0x00);
	}
}

static u_char
fakeppc_io(device_t dev, int opcode, u_char *addr, int cnt, u_char byte)
{
	struct fakeppc_softc *sc = device_get_softc(dev);

	switch (opcode) {
	case PPB_RSTR:
		return (hx_rstr(sc));
	case PPB_RDTR:
		return (0x55);
	case PPB_WDTR:
		sc->last_wdtr = byte;
		if (byte == (u_char)NIBBLE_1284_REQUEST_ID)
			sc->phase = PH_NEG;
		break;
	case PPB_WCTR:
		sc->last_wctr = byte;
		if (sc->phase == PH_READ && byte == 0x04) {
			sc->nib_sel ^= 1;
			if (sc->nib_sel == 0)
				sc->byte_i++;
		}
		break;
	default:
		break;
	}
	return (0);
}

static int fakeppc_setmode(device_t dev, int mode)
{
	struct fakeppc_softc *sc = device_get_softc(dev);

	if (mode != PPB_COMPATIBLE && sc->phase == PH_NEG) {
		sc->phase = PH_READ;
		sc->byte_i = 0;
		sc->nib_sel = 0;
	}
	if (mode == PPB_COMPATIBLE)
		sc->phase = PH_IDLE;
	return (0);
}

static int fakeppc_exec_microseq(device_t dev, struct ppb_microseq **msq) { return (0); }
static int fakeppc_reset_epp(device_t dev) { return (0); }
static int fakeppc_ecp_sync(device_t dev) { return (0); }
static int fakeppc_read(device_t dev, char *buf, int len, int how) { return (0); }
static int fakeppc_write(device_t dev, char *buf, int len, int how) { return (0); }

static void
fakeppc_identify(driver_t *drv, device_t parent)
{
	if (device_find_child(parent, "ppc", 0) == NULL)
		BUS_ADD_CHILD(parent, parent, 0, "ppc", 0);
}

static int
fakeppc_probe(device_t dev)
{
	device_set_desc(dev, "scripted IEEE1284 peer (ppbharness3)");
	return (0);
}

static int fakeppc_attach(device_t dev);

static device_method_t fakeppc_methods[] = {
	DEVMETHOD(device_identify,	fakeppc_identify),
	DEVMETHOD(device_probe,		fakeppc_probe),
	DEVMETHOD(device_attach,	fakeppc_attach),

	DEVMETHOD(ppbus_io,		fakeppc_io),
	DEVMETHOD(ppbus_exec_microseq,	fakeppc_exec_microseq),
	DEVMETHOD(ppbus_reset_epp,	fakeppc_reset_epp),
	DEVMETHOD(ppbus_setmode,	fakeppc_setmode),
	DEVMETHOD(ppbus_ecp_sync,	fakeppc_ecp_sync),
	DEVMETHOD(ppbus_read,		fakeppc_read),
	DEVMETHOD(ppbus_write,		fakeppc_write),

	DEVMETHOD_END
};

static driver_t fakeppc_driver = {
	"ppc",
	fakeppc_methods,
	sizeof(struct fakeppc_softc),
};

static devclass_t fakeppc_devclass;

DRIVER_MODULE(fakeppc, nexus, fakeppc_driver, fakeppc_devclass, NULL, NULL);

/* ------------------------------------------------------------------ */
/* hxc: dummy ppbus child (real ppb_device ivars via ppbus_add_child)  */
/* ------------------------------------------------------------------ */

static int hxc_probe(device_t dev) { device_set_desc(dev, "ppbus child"); return (0); }
static int hxc_attach(device_t dev) { return (0); }

static device_method_t hxc_methods[] = {
	DEVMETHOD(device_probe,		hxc_probe),
	DEVMETHOD(device_attach,	hxc_attach),
	DEVMETHOD_END
};

static driver_t hxc_driver = { "hxc3", hxc_methods, 0 };
static devclass_t hxc_devclass;
DRIVER_MODULE(hxc3, ppbus, hxc_driver, hxc_devclass, NULL, NULL);

/* ------------------------------------------------------------------ */
/* test orchestration                                                  */
/* ------------------------------------------------------------------ */

static int ppbx3_test;			/* sysctl hw.ppbx3.test */
static int ppbx3_racetrials;
static int ppbx3_lostwake;
static int ppbx3_caught;

SYSCTL_NODE(_hw, OID_AUTO, ppbx3, CTLFLAG_RW, 0, "ppbharness3");
SYSCTL_INT(_hw_ppbx3, OID_AUTO, test, CTLFLAG_RW, &ppbx3_test, 0,
    "1=DF2327-nocolon 2=DF2328-noNUL 3=DF2329-race 4=control");
SYSCTL_INT(_hw_ppbx3, OID_AUTO, racetrials, CTLFLAG_RD, &ppbx3_racetrials, 0,
    "DF2329 trials so far");
SYSCTL_INT(_hw_ppbx3, OID_AUTO, lostwake, CTLFLAG_RD, &ppbx3_lostwake, 0,
    "DF2329 lost-wakeup catches");
SYSCTL_INT(_hw_ppbx3, OID_AUTO, caught, CTLFLAG_RD, &ppbx3_caught, 0,
    "1 when the current waiter is stuck on a free bus");

static device_t ppcdev_g, ppbus_g, devA_g, devB_g;
static int ppbus_unit;

static void
hx_arm_and_attach(device_t ppcdev, device_t ppbusdev, const char *id,
    int idlen)
{
	struct fakeppc_softc *sc = device_get_softc(ppcdev);

	bzero(sc, sizeof(*sc));
	sc->phase = PH_IDLE;
	memcpy(sc->stream, id, idlen);
	sc->stream_len = idlen;

	device_probe_and_attach(ppbusdev);
}

/* ---- DF-2329: lost-wakeup race ---- */

struct r2329 {
	volatile int	go;
	volatile int	waiter_done;
	volatile int	waiter_ret;
	int		delay_us;
};

static struct r2329 R;

static void
hx_waiter(void *arg)
{
	struct r2329 *r = arg;

	/* run on a DIFFERENT cpu than the racer so the release can land
	 * while we are between crit_exit() and tsleep() */
	lwkt_migratecpu(1);

	for (;;) {
		while (!r->go)
			tsleep(&r->go, 0, "hxgo", 1);
		r->go = 0;
		r->waiter_ret = ppb_request_bus(ppbus_g, devB_g,
		    PPB_WAIT | PPB_NOINTR);
		if (r->waiter_ret == 0)
			ppb_release_bus(ppbus_g, devB_g);
		r->waiter_done = 1;
		wakeup(&r->waiter_done);
	}
	kthread_exit();
}

static void
hx_racer(void *arg)
{
	struct r2329 *r = arg;
	struct ppb_data *ppb = (struct ppb_data *)device_get_softc(ppbus_g);
	struct thread *td;
	int trial, spin;

	kprintf("PPBX3: DF-2329 lost-wakeup race: owner releases between "
	    "requester crit_exit (ppbconf.c:467) and tsleep (:474); "
	    "wakeup has no interlock -> lost\n");
	kthread_create(hx_waiter, r, &td, "hxw2329");
	tsleep(r, 0, "hxsync", hz / 10);		/* let waiter start */

	for (trial = 0; trial < 50000; trial++) {
		if (ppb_request_bus(ppbus_g, devA_g, PPB_DONTWAIT) != 0)
			continue;

		r->waiter_done = 0;
		r->delay_us = trial % 400;	/* sweep 0..399 us */
		r->go = 1;
		wakeup(&r->go);

		/* burn delay_us while the waiter is somewhere between its
		 * owner check (inside crit) and its tsleep() */
		DELAY(r->delay_us);

		ppb_release_bus(ppbus_g, devA_g);	/* owner=0 + wakeup(ppb) */

		/* fast-poll up to ~250us for the waiter to finish */
		for (spin = 0; spin < 5 && !r->waiter_done; spin++)
			DELAY(50);
		if (!r->waiter_done)
			tsleep(&r->waiter_done, 0, "hxdone", 1);

		ppbx3_racetrials = trial + 1;
		if (r->waiter_done == 0 && ppb->ppb_owner == NULL) {
			ppbx3_lostwake++;
			ppbx3_caught = 1;
			kprintf("PPBX3: DF-2329 REPRODUCED at trial %d "
			    "(delay %d us): waiter STILL ASLEEP with "
			    "PPB owner==NULL -- wakeup was lost; "
			    "PPB_WAIT|PPB_NOINTR sleep is uninterruptible "
			    "(kill-proof)\n", trial, r->delay_us);
			/* revive: emulate a later release so the module
			 * can continue (production has none) */
			wakeup(ppb);
			for (spin = 0; spin < 100 && !r->waiter_done; spin++)
				DELAY(50);
			ppbx3_caught = 0;
			break;
		}
		/* drain the waiter for the next trial */
		for (spin = 0; spin < 100 && !r->waiter_done; spin++)
			DELAY(50);
	}
	kprintf("PPBX3: DF-2329 done: trials=%d lostwake=%d\n",
	    ppbx3_racetrials, ppbx3_lostwake);
	kthread_exit();
}

static void
hx_worker(void *arg)
{
	struct thread *td;
	char id2327[16];
	char id2328[256];
	static const char idctl[] = "MFG:X;MDL:Y;VER:1;CLS:PRINTER;";
	int i;

	kprintf("PPBX3: harness worker start (production ppbconf.c "
	    "verbatim)\n");

	ppcdev_g = devclass_get_device(devclass_find("ppc"), 0);
	if (ppcdev_g == NULL) {
		kprintf("PPBX3: no fake ppc device\n");
		kthread_exit();
	}
	ppbus_unit = 0;
	ppbus_g = device_add_child(ppcdev_g, "ppbus", ppbus_unit);

	/* DF-2327: "MFG;MFG" -- ';'->'\0' at ppbconf.c:236-237 leaves
	 * "MFG\0MFG\0"; search_token(str,len,"MFG") hits at 0, then
	 * search_token(str,UNKNOWN_LENGTH,":") scans "MFG" only -> NULL
	 * -> +1 -> kprintf("%s", (char*)0x1) -> Fatal trap 12 */
	strcpy(id2327, "MFG;MFG");

	/* DF-2328: 256 non-NUL bytes, "REV" at 252, no ':' anywhere:
	 * ':'-scan walks str[252..255] -> str[256] (uninit: read_id bzeros
	 * only PPB_PnP_STRING_SIZE=256 bytes) -> stack above the object */
	memset(id2328, 'A', sizeof(id2328));
	id2328[252] = 'R'; id2328[253] = 'E'; id2328[254] = 'V';
	/* id2328[255] stays 'A'; NOTE total stream_len = 256 -> no NUL */

	for (i = 0; ; i++) {
		int t = ppbx3_test;
		if (t == 1) {
			/* fresh ppbus child so the crafted ID is really
			 * probed (an already-attached ppbus would no-op) */
			ppbus_g = device_add_child(ppcdev_g, "ppbus",
			    ++ppbus_unit);
			kprintf("PPBX3: arming DF-2327 (MFG;MFG -- no colon) "
			    "attach NOW; stock: Fatal trap 12 at "
			    "kprintf-via-NULL+1\n");
			hx_arm_and_attach(ppcdev_g, ppbus_g, id2327,
			    strlen(id2327));
			kprintf("PPBX3: DF-2327 attach returned -- no trap?! "
			    "(not reproduced)\n");
			ppbx3_test = 0;
		} else if (t == 2) {
			ppbus_g = device_add_child(ppcdev_g, "ppbus",
			    ++ppbus_unit);
			kprintf("PPBX3: arming DF-2328 (256B NUL-free ID, "
			    "REV at 252, no colon) attach NOW; stock: OOB "
			    "walk past str[255] -> NULL+1 trap\n");
			hx_arm_and_attach(ppcdev_g, ppbus_g, id2328, 256);
			kprintf("PPBX3: DF-2328 attach returned -- no trap?! "
			    "(not reproduced)\n");
			ppbx3_test = 0;
		} else if (t == 3) {
			ppbx3_test = 0;
			/* attach ppbus first (benign control ID) so the
			 * ppbus driver methods exist for the children */
			hx_arm_and_attach(ppcdev_g, ppbus_g, idctl,
			    strlen(idctl));
			kprintf("PPBX3: control attach for test3 done\n");
			devA_g = BUS_ADD_CHILD(ppbus_g, ppbus_g, 0, "hxc3", 0);
			devB_g = BUS_ADD_CHILD(ppbus_g, ppbus_g, 1, "hxc3", 1);
			if (devA_g == NULL || devB_g == NULL) {
				kprintf("PPBX3: FAILED to add hxc3 children\n");
				continue;
			}
			device_probe_and_attach(devA_g);
			device_probe_and_attach(devB_g);
			kthread_create(hx_racer, &R, &td, "hxr2329");
		} else if (t == 4) {
			ppbx3_test = 0;
			kprintf("PPBX3: control probe (well-formed ID)\n");
			hx_arm_and_attach(ppcdev_g, ppbus_g, idctl,
			    strlen(idctl));
			kprintf("PPBX3: control attach OK (no trap)\n");
		}
		tsleep(&ppbx3_test, 0, "hxidle", hz);
	}
	kthread_exit();
}

static int
fakeppc_attach(device_t dev)
{
	struct thread *td;

	if (device_get_unit(dev) != 0)
		return (0);
	kprintf("PPBX3: fakeppc0 attached, starting worker\n");
	kthread_create(hx_worker, NULL, &td, "ppbhx3");
	return (0);
}