compat/snprintf: don't look at va_list twice

If you define SNPRINTF_RETURNS_BOGUS, we use a special git_vsnprintf wrapper assumes that vsnprintf returns "-1" instead of the number of characters that you would need to store the result. To do this, it invokes vsnprintf multiple times, growing a heap buffer until we have enough space to hold the result. However, this means we evaluate the va_list parameter multiple times, which is generally a bad thing (it may be modified by calls to vsnprintf, yielding undefined behavior). Instead, we must va_copy it and hand the copy to vsnprintf, so we always have a pristine va_list. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed Dec 12, 2011 at 09:25 UTC a9bfbc5b698103b1553e1023c4f77001cc861e79
1 file changed +7 -2
compat/snprintf.c
+7 -2
@@ -19,11 +19,14 @@
19 #undef vsnprintf
20 int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
21 {
22 + va_list cp;
23 char *s;
24 int ret = -1;
25
26 if (maxsize > 0) {
26 - ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
27 + va_copy(cp, ap);
28 + ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
29 + va_end(cp);
30 if (ret == maxsize-1)
31 ret = -1;
32 /* Windows does not NUL-terminate if result fills buffer */
@@ -42,7 +45,9 @@ int git_vsnprintf(char *str, size_t maxsize, const char *format, va_list ap)
45 if (! str)
46 break;
47 s = str;
45 - ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, ap);
48 + va_copy(cp, ap);
49 + ret = vsnprintf(str, maxsize-SNPRINTF_SIZE_CORR, format, cp);
50 + va_end(cp);
51 if (ret == maxsize-1)
52 ret = -1;
53 }