[libvirt] [PATCH] buf: Fix possible infinite loop in EscapeString, VSnprintf
Cole Robinson
crobinso at redhat.com
Wed Sep 1 21:43:06 UTC 2010
On 09/01/2010 04:38 PM, Eric Blake wrote:
> On 09/01/2010 01:43 PM, Cole Robinson wrote:
>> The current code will go into an infinite loop if the printf generated
>> string is>= 1000, AND exactly 1 character smaller than the amount of free
>> space in the buffer. When this happens, we are dropped into the loop body,
>> but nothing will actually change, because count == (buf->size - buf->use - 1),
>> and virBufferGrow returns unchanged if count< (buf->size - buf->use)
>>
>> Fix this by removing the '- 1' bit from 'size'. The *nprintf functions handle
>> the NULL byte for us anyways, so we shouldn't need to manually accomodate
>> for it.
>
> Per 'man vsnprintf',
>
>> The functions snprintf() and vsnprintf() do not write more than size
>> bytes (including the trailing '\0'). If the output was truncated due
>> to this limit then the return value is the number of characters (not
>> including the trailing '\0') which would have been written to the final
>> string if enough space had been available. Thus, a return value of
>> size or more means that the output was truncated. (See also below
>> under NOTES.)
>
> So, if the result is == size, then we MUST re-alloc and try again, to
> avoid truncation.
>
>> - size = buf->size - buf->use - 1;
>> + size = buf->size - buf->use;
>> va_start(argptr, format);
>> va_copy(locarg, argptr);
>> while (((count = vsnprintf(&buf->content[buf->use], size, format,
>
> This makes sense - we might as well tell vsnprintf exactly how many
> bytes it has to play with.
>
>> - locarg))< 0) || (count>= size - 1)) {
>> + locarg))< 0) || (count>= size)) {
>
> However, I think this still has issues. vsnprintf returns -1 ONLY when
> there is a non-recoverable error (EILSEQ, ENOMEM), and NOT when the
> string was truncated. So if it returns -1 once, it will ALWAYS return
> -1 no matter how much larger we make the buffer. (At least, since we
> use the gnulib module, it will always obey POSIX. If it weren't for
> gnulib, then yes, I could see how you could worry about older glibc that
> didn't obey POSIX and returned -1 instead of the potential print size).
> So why not write it as an if() instead of a while(), since we really
> only need one truncated vsnprintf attempt before guaranteeing that the
> buffer is large enough for the second attempt.
>
> [Side note: I'm really starting to get annoyed by the Thunderbird bug
> that corrupts spacing of quoted text that contains & or <. Sorry to
> everyone else that has to put up with my review comments that have been
> mangled.]
>
>> @@ -250,13 +250,12 @@ virBufferVSprintf(const virBufferPtr buf, const char *format, ...)
>> return;
>> }
>>
>
> You're missing a step. It's okay that size is 1 larger, but you also
> need to make the virBufferGrow(buf, grow_size) guarantee that it
> allocates at least count+1 bytes, to avoid a needless second iteration
> through the loop. Back to that comment of an if() being better than a
> while() loop.
>
>> - size = buf->size - buf->use - 1;
>> + size = buf->size - buf->use;
>> va_copy(locarg, argptr);
>> }
>> va_end(argptr);
>> va_end(locarg);
>> buf->use += count;
>> - buf->content[buf->use] = '\0';
>
> Agree with this part.
>
>
>> }
>>
>> /**
>> @@ -340,19 +339,18 @@ virBufferEscapeString(const virBufferPtr buf, const char *format, const char *st
>> return;
>> }
>>
>> - size = buf->size - buf->use - 1;
>> + size = buf->size - buf->use;
>> while (((count = snprintf(&buf->content[buf->use], size, format,
>> - (char *)escaped))< 0) || (count>= size - 1)) {
>> + (char *)escaped))< 0) || (count>= size)) {
>> buf->content[buf->use] = 0;
>> grow_size = (count> 1000) ? count : 1000;
>> if (virBufferGrow(buf, grow_size)< 0) {
>
> Same problem on while() vs. if(), and not allocating a large enough buffer.
>
> NACK as written, but this is indeed a serious bug, so looking forward to
> v2 (I can help you write it, if needed).
>
THanks for the review (online and offline). I've sent an updated patch
with your recommended improvements.
Thanks,
Cole
More information about the libvir-list
mailing list