# DF-4139 — `throw_rude_remark()` strcpy of a up-to-1024-byte message into
# `ioctl_reply->msg[256]` (up to 4 bytes past the VINUM_CREATE ioctl
# buffer), plus a dangling static `ioctl_reply` after longjmp/remove

## Root cause
`sys/dev/raid/vinum/vinumconfig.c:108-117`:

```c
    if ((ioctl_reply != NULL)
    &&(!(vinum_conf.flags & VF_READING_CONFIG))) {
	    kvasnprintf(&text, MSG_MAX, msg, ap);	/* up to 1024 bytes */
	    strcpy(ioctl_reply->msg, text);		/* msg is char[256]! */
	    ioctl_reply->error = error;
```

* `MSG_MAX` is 1024 (vinumconfig.c:90) but the destination is
  `struct _ioctl_reply { int error; char msg[MAX_IOCTL_REPLY]; }` with
  `MAX_IOCTL_REPLY` **256** in non-VINUMDEBUG builds (vinumio.h:42-45).
* For `VINUM_CREATE` the reply struct overlays the 1024-byte ioctl buffer
  (`BUFSIZE`, vinumio.h:113,116), so `strcpy` writes at `data+4` and a
  maximally long message (e.g. `throw_rude_remark(EINVAL, "Invalid
  configuration information: %s", token[0])` with a ~988-byte first
  token) writes `4 + 1024 = 1028` bytes into the kmalloc(1024) buffer —
  **4 bytes past the end of the kernel heap allocation**
  (sys/kern/sys_generic.c:675 allocates exactly 1024 for this ioctl).
  The last 3 attacker-controlled characters plus the NUL land in the
  adjacent kmalloc object.
* Secondary lifetime defect: `parse_user_config()` clears the static
  `ioctl_reply` only on the normal path (vinumconfig.c:1683).  When
  `parse_config` longjmps out via `throw_rude_remark`, and after every
  `remove()` call (vinumconfig.c:1693 never clears it), the static keeps
  pointing at the ioctl buffer, which `sys_ioctl` frees on return.  A
  later `throw_rude_remark` reached from a different ioctl while another
  thread holds the config lock (VINUM_ATTACH/DETACH/MOVE paths call
  `give_sd_to_plex`/`give_sd_to_drive`, which throw) then performs a
  **use-after-free write** of the message text into the freed 260/1024-
  byte chunk.  (Without the lock held it panics first — see DF-4136.)

## Threat
Root/operator via /dev/vinum/control.  The 4-byte overflow is small but
is a genuine cross-object heap write with chosen ASCII content.

## Fix
```diff
--- a/sys/dev/raid/vinum/vinumconfig.c
+++ b/sys/dev/raid/vinum/vinumconfig.c
@@ -112,7 +112,7 @@ throw_rude_remark(int error, char *msg,...)
 	 * it may contain our input parameters
 	 */
 	    kvasnprintf(&text, MSG_MAX, msg, ap);
-	    strcpy(ioctl_reply->msg, text);
+	    strlcpy(ioctl_reply->msg, text, sizeof(ioctl_reply->msg));
```
and clear the static `ioctl_reply = NULL` in a setjmp-safe place (e.g. at
the top of the VINUM_CREATE error branch in vinumioctl.c and at the end
of `remove()`).

## Verification
Skipped live: Low severity (4-byte overflow, root-gated); the dangling
static UAF additionally requires a cross-thread lock race.  Bounds math
is certain from source (MSG_MAX vs MAX_IOCTL_REPLY vs BUFSIZE).
