Skip to content

Commit 6d6b2c3

Browse files
committed
crypto: rng - Skip leading zero-length iovec segments
The fast per-CPU DRBG path computes its initial user destination address straight from the iov_iter. For an ITER_IOVEC iter it reads iter_iov_addr() and iter_iov_len() of the current segment, but when the iovec leads with one or more zero-length segments, the current segment is one of those empty entries. iter_iov_addr() then hands back the base of an empty segment, which is whatever userspace put there: its base can be NULL or some other unwritable address, since a zero-length segment is never actually touched. Right after the setup, that address is prefaulted, and on a bogus base it fails. A failed prefault on the very first address is treated as fatal, so the whole read bails out to -EFAULT even though there are perfectly good non-empty segments later in the iovec. This is reachable with something as simple as readv() on /dev/urandom where the first iovec entry is {NULL, 0}. Fix it by advancing the iterator by zero before reading the first address. The iovec advance loop walks past every leading empty segment and stops at the first non-empty one, and there's guaranteed to be such a segment because iov_iter_count() is nonzero at this point. Empty segments that crop up mid-stream are already skipped by the per-copy advance, so this only needs to run once during setup. Assisted-by: Claude:claude-opus-4-8 Signed-off-by: Sultan Alsawaf <sultan@ciq.com>
1 parent 08c45ae commit 6d6b2c3

1 file changed

Lines changed: 10 additions & 0 deletions

File tree

crypto/rng.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,16 @@ static ssize_t crypto_devrandom_read_iter(struct iov_iter *iter, bool reseed)
439439
uaddr = iter->ubuf + iter->iov_offset;
440440
ulen = iov_iter_count(iter);
441441
} else if (iter_is_iovec(iter)) {
442+
/*
443+
* Skip any leading zero-length segments first, since
444+
* uaddr would otherwise point at an empty segment's
445+
* base (which may be NULL or unwritable). Advancing by
446+
* zero walks past every leading empty segment (the
447+
* iovec advance loop stops at the first non-empty one),
448+
* and there's guaranteed to be a non-empty one because
449+
* iov_iter_count() is nonzero above.
450+
*/
451+
iov_iter_advance(iter, 0);
442452
uaddr = iter_iov_addr(iter);
443453
ulen = iter_iov_len(iter);
444454
} else {

0 commit comments

Comments
 (0)