diff --git a/testing/unreliable-libc/file.c b/testing/unreliable-libc/file.c index ab6d934f7..c894e7d5a 100644 --- a/testing/unreliable-libc/file.c +++ b/testing/unreliable-libc/file.c @@ -7,6 +7,8 @@ #include #include #include +#include +#include static double probabilities[] = { [ENOSPC] = 0.01, @@ -64,6 +66,72 @@ ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset) if (libc_pwritev == NULL) { libc_pwritev = dlsym(RTLD_NEXT, "pwritev"); } + + /* If no vectors or invalid count, forward directly. */ + if (iov == NULL || iovcnt <= 0) { + return libc_pwritev(fd, iov, iovcnt, offset); + } + + if (iovcnt > 0 && chance(short_write_probability)) { + struct iovec iov_copy[iovcnt]; + memcpy(iov_copy, iov, sizeof(struct iovec) * iovcnt); + + /* Compute total bytes requested (guarding against overflow). */ + size_t total = 0; + for (int i = 0; i < iovcnt; ++i) { + size_t len = iov_copy[i].iov_len; + if (len > 0 && total > SIZE_MAX - len) { + total = SIZE_MAX; + break; + } + total += len; + } + + /* Only meaningful to short-write if total > 1. */ + if (total > 1) { + /* Choose cutoff in [1, total-1] */ + size_t cutoff = 1 + (size_t)(lrand48() % (total - 1)); + size_t remaining = cutoff; + + /* Walk iov_copy and reduce lengths to exactly match cutoff. + * After the cutoff is satisfied, following iov entries will be set to 0 length. + */ + for (int i = 0; i < iovcnt; ++i) { + size_t len = iov_copy[i].iov_len; + if (len == 0) { + /* keep zero-length entries as zero, they do not consume remaining */ + continue; + } + if (remaining == 0) { + /* drop the rest */ + iov_copy[i].iov_len = 0; + continue; + } + if (len <= remaining) { + /* fully include this iovec */ + remaining -= len; + /* keep iov_copy[i].iov_len as-is */ + } else { + /* partial include: shorten this iovec to remaining */ + iov_copy[i].iov_len = remaining; + remaining = 0; + } + } + + /* Compute new iovcnt to avoid passing trailing zero-length iovecs */ + int new_iovcnt = iovcnt; + while (new_iovcnt > 0 && iov_copy[new_iovcnt - 1].iov_len == 0) { + new_iovcnt--; + } + /* Defensive: cutoff >= 1 so new_iovcnt should be > 0; This should always be true*/ + if (new_iovcnt > 0) { + printf("%s: injecting fault SHORT WRITE cutoff=%zu of %zu (new_iovcnt=%d)\n", + __func__, cutoff, total, new_iovcnt); + return libc_pwritev(fd, iov_copy, new_iovcnt, offset); + } + } + } + if (inject_fault(ENOSPC)) { printf("%s: injecting fault NOSPC\n", __func__); return -1;