# DF-4137 — `remove_plex_entry()` compacts `vol->plex[]` with a byte # count instead of an element count, and decrements `vol->plexes` # unconditionally ## Root cause `sys/dev/raid/vinum/vinumconfig.c:1841-1853`: ```c for (myplexno = 0; myplexno < vol->plexes; myplexno++) if (vol->plex[myplexno] == plexno) /* found it */ break; if (myplexno == vol->plexes) /* didn't find it. Huh? */ log(LOG_ERR, ...); /* ... but execution continues! */ if (myplexno < (vol->plexes - 1)) /* not the last plex */ bcopy(&vol->plex[myplexno + 1], &vol->plex[myplexno], vol->plexes - 1 - myplexno); /* <-- count, not bytes */ vol->plexes--; /* <-- even when not found */ ``` Two defects: 1. The bcopy length is `vol->plexes - 1 - myplexno` **ints expressed in bytes** — it copies only a quarter of the required data (compare the correct form in remove_sd_entry, vinumconfig.c:1780-1782, which multiplies by `sizeof(int)`). For plex indices < 256 the corrupted entry accidentally retains the right value (little-endian LSB copy); from plex number 256 upward (plexes_allocated grows by 8 per EXPAND, reachable with ~33 plex lines) the volume's plex list silently points at the wrong plex — I/O is routed to a foreign plex (data corruption). 2. When the plex is *not found* in its own volume's list (reachable via the index/plexno confusion written by the `kw_prefer` bug DF-1399, or dangling references after partial config), the error is only logged and `vol->plexes` is decremented anyway — repeatable removals drive `vol->plexes` negative, and `vol->plex[vol->plexes] = plexno` in `give_plex_to_volume` (vinumconfig.c:205) then writes before the array. ## Fix ```diff --- a/sys/dev/raid/vinum/vinumconfig.c +++ b/sys/dev/raid/vinum/vinumconfig.c @@ -1848,11 +1848,12 @@ remove_plex_entry(int plexno, int force, int recurse) if (myplexno == vol->plexes) /* didn't find it. Huh? */ log(LOG_ERR, "Error removing plex %s: not found in volume %s\n", plex->name, vol->name); + else { if (myplexno < (vol->plexes - 1)) /* not the last plex in the list */ bcopy(&vol->plex[myplexno + 1], &vol->plex[myplexno], - vol->plexes - 1 - myplexno); + (vol->plexes - 1 - myplexno) * sizeof(int)); vol->plexes--; + } ``` ## Verification Analysis-only (skipped live): Low severity; needs >=256 allocated plexes to corrupt an index in an observable way (bookkeeping-heavy setup), and the negative-`plexes` route piggybacks on DF-1399's state. The missing-`sizeof(int)` is directly visible against the correct twin in remove_sd_entry:1780.