Subject: Re: [PATCH] wrapper: properly handle MAX_IO_SIZE in `write_in_full()`

From: Junio C Hamano <[email-masked]>
Date: Thu, 09 Apr 2026 14:09:42 -0700

Jeff King <[email-masked]> writes:

> On Thu, Apr 09, 2026 at 01:40:36PM -0700, Junio C Hamano wrote:
>
>> >> As the width of ssize_t in bits can be a lot smaller than size_t,
>> >> the above "unsigned_add_overflows() triggers way too late for the
>> >> check to matter, no?
>> >
>> > I think it is correct as-is.
>> >
>> > The real check against ssize_t is later, when we compare total_length to
>> > MAX_IO_SIZE (which is clamped to SSIZE_MAX). So this is just making sure
>> > we do not overflow size_t when counting up the total (and if we do, we
>> > _know_ we are going to overflow ssize_t, which must be smaller).
>> 
>> But then what happens after it breaks out of the loop?  We cannot be
>> at i==0, so let's say we have a reasonably small iov[0] and iov[1]
>> that is so large and makes size_t wraparound.  We break out here,
>> and then send the iov[0] with writev().  But have we checked if
>> iov[0] is under MAX_IO_SIZE in that case before calling writev()?
>
> I think so. Either:
>
>   - We completed the first iteration of the loop successfully (and i >=
>     1), in which case we added iov[0].iov_len to total_length, and then
>     compared total_length against MAX_IO_SIZE, but did not break out of
>     the loop. So we know iov[0] is within the limits.
>
>   - We bailed at i==0 either because of addition overflow, or because of
>     the MAX_IO_SIZE check. Either way, we will bail to xwrite() because
>     i is 0.

Yup, you're right.

There is no addition overflow at i==0, but I do not think we can
construct a case where the sum is not checked against MAX_IO_SIZE
before the vector is passed to underlying writev().

iov[0].iov_len that is slightly smaller than MAX_IO_SIZE would allow
us to keep looping to i==1 at which time iov[1].iov_len is so big
that we may trigger unsigned_add_overflows() check, but then what we
send to writev() is the first segment, which is smaller than
MAX_IO_SIZE, so we are OK.

iov[0].iov_len that is slightly larger than MAX_IO_SIZE would stop
us moving to i==1 at the end of the loop, and directly punt to
xwrite(), so we are OK, too.