Fortify vsnprintf in more cases.

Bug: http://b/30445072
Change-Id: I1893890f0e3b56533eef053eda1bd96a0b9a5119
diff --git a/libc/stdio/stdio.cpp b/libc/stdio/stdio.cpp
index c673611..b709b40 100644
--- a/libc/stdio/stdio.cpp
+++ b/libc/stdio/stdio.cpp
@@ -46,6 +46,7 @@
 
 #include "local.h"
 #include "glue.h"
+#include "private/bionic_fortify.h"
 #include "private/ErrnoRestorer.h"
 #include "private/thread_private.h"
 
@@ -779,7 +780,7 @@
 }
 
 int sprintf(char* s, const char* fmt, ...) {
-  PRINTF_IMPL(vsnprintf(s, INT_MAX, fmt, ap));
+  PRINTF_IMPL(vsprintf(s, fmt, ap));
 }
 
 int sscanf(const char* s, const char* fmt, ...) {
@@ -802,8 +803,34 @@
   return vfscanf(stdin, fmt, ap);
 }
 
+int vsnprintf(char* s, size_t n, const char* fmt, va_list ap) {
+  // stdio internals use int rather than size_t.
+  static_assert(INT_MAX <= SSIZE_MAX, "SSIZE_MAX too large to fit in int");
+
+  __check_count("vsnprintf", "size", n);
+
+  // Stdio internals do not deal correctly with zero length buffer.
+  char dummy;
+  if (n == 0) {
+    s = &dummy;
+    n = 1;
+  }
+
+  FILE f;
+  __sfileext fext;
+  _FILEEXT_SETUP(&f, &fext);
+  f._file = -1;
+  f._flags = __SWR | __SSTR;
+  f._bf._base = f._p = reinterpret_cast<unsigned char*>(s);
+  f._bf._size = f._w = n - 1;
+
+  int result = __vfprintf(&f, fmt, ap);
+  *f._p = '\0';
+  return result;
+}
+
 int vsprintf(char* s, const char* fmt, va_list ap) {
-  return vsnprintf(s, INT_MAX, fmt, ap);
+  return vsnprintf(s, SSIZE_MAX, fmt, ap);
 }
 
 int vwprintf(const wchar_t* fmt, va_list ap) {