os::vsnprintf is a wrapper around the platform vsnprintf call, which is declared as follows:
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
and specified as:
RETURN VALUE
Upon successful return, these functions return the number of characters printed (excluding the null byte used to
end output to strings).
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte
('\0')). If the output was truncated due to this limit, then the return value is the number of characters
(excluding the terminating null byte) 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.
If an output error is encountered, a negative value is returned.
The use of `int` as the return type here means that it cannot operate correctly if the formatted string would require a buffer of length > INT_MAX. In that case we find that -1 gets returned even though the inability to represent the required length is not listed as a potential failure mode (-1 is supposed to indicate a conversion error). We have also observed that different implementations behave differently under these circumstances - specifically Posix systems seem to fill the buffer with what they can, whereas Windows leaves the buffer empty.
Many callers of os::vsnprintf will assert that the return value of positive and so will fail when huge strings are involved. Many callers pass in a limited size buffer knowing that the result will be truncated to the buffer size, which is fine, but they don't expect the call to fail because it wants to report how big a buffer was actually needed - these callers do not care about that.
It is not easy to handle this problem at the os::vsnprintf level however as it has no knowledge of the actual format string nor its arguments. So we have to ensure that callers limit the potential formatted string, not just the size of the output buffer to use. So every caller of os::vsnprintf that checks the return value (many don't!) needs to be checked to see if it asserts on a -1 return; which transitively means we have to check all the callers of jio_snprintf and jio_vsnprintf as well.
int vsnprintf(char *str, size_t size, const char *format, va_list ap);
and specified as:
RETURN VALUE
Upon successful return, these functions return the number of characters printed (excluding the null byte used to
end output to strings).
The functions snprintf() and vsnprintf() do not write more than size bytes (including the terminating null byte
('\0')). If the output was truncated due to this limit, then the return value is the number of characters
(excluding the terminating null byte) 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.
If an output error is encountered, a negative value is returned.
The use of `int` as the return type here means that it cannot operate correctly if the formatted string would require a buffer of length > INT_MAX. In that case we find that -1 gets returned even though the inability to represent the required length is not listed as a potential failure mode (-1 is supposed to indicate a conversion error). We have also observed that different implementations behave differently under these circumstances - specifically Posix systems seem to fill the buffer with what they can, whereas Windows leaves the buffer empty.
Many callers of os::vsnprintf will assert that the return value of positive and so will fail when huge strings are involved. Many callers pass in a limited size buffer knowing that the result will be truncated to the buffer size, which is fine, but they don't expect the call to fail because it wants to report how big a buffer was actually needed - these callers do not care about that.
It is not easy to handle this problem at the os::vsnprintf level however as it has no knowledge of the actual format string nor its arguments. So we have to ensure that callers limit the potential formatted string, not just the size of the output buffer to use. So every caller of os::vsnprintf that checks the return value (many don't!) needs to be checked to see if it asserts on a -1 return; which transitively means we have to check all the callers of jio_snprintf and jio_vsnprintf as well.