D535 - strdup covered, and a missing terminator is only caught by luck

measured - 2026-09-04 (eight cases, and a break run three times)

differential   400  ->  408 cases

strdup and strndup were the last genuinely differentiable pair from D532's census. They are covered now, and the more useful half of this is what the break said about how well.

The pointer is not compared; the contents are

An address is a fact about one process - the same reasoning the search family already follows, one step on. What crosses is found-or-not, then the copy through its terminator, because the terminator is the half of strndup that is easy to lose.

The discriminating case has to have a source longer than the bound. A strncpy-shaped implementation is correct whenever the source fits, so every case where it does passes for a broken one.

And then the break did not behave

Removing the terminator and running three times:

run 1   strdup/embedded-high-byte  strndup/bound-longer-than-source  strndup/truncates
run 2   strdup/embedded-high-byte  strdup/plain  strndup/bound-longer-than-source
run 3   strdup/embedded-high-byte  strdup/plain  strndup/bound-longer-than-source  strndup/truncates

Three, three and four - and different sets. The byte after an unterminated copy is whatever the allocator last left there, and it is a zero often enough that the read finds a terminator that was never written.

So the claim had to be narrowed

The doc comment said the unterminated marker made this deterministic. It does not. What these cases actually verify is the contents of a copy that is terminated; termination itself is caught probabilistically.

That distinction is the whole value of running the break more than once. A single run showed four failures and would have been written up as "the cases catch it" - which is the confident wrong answer this file exists to avoid, in a decision about a test.

Catching it properly needs the allocation poisoned before the call. That is the reference's technique for buffers it owns (D511's snprintf window, strncpy's padding) and it is not available to a caller of strdup, which does not choose the memory. Recorded as a limit rather than worked around.

The rule this sharpens

"A guard is not finished until somebody has made it fail" has an unstated second half: a break that fires is not the same as a break that fires reliably. Where the failure depends on memory nobody wrote, run it more than once - the same reason three samples is the floor for a run, applied to a test.