Make core/fxcrt compliant with UNSAFE_BUFFERS() macro.

First application is the (very C-style files) in core/fxcrt. This
CL flags the places which have still not been able to be converted.

Change-Id: I16652f7f9d065f499392bf64b993cb19854ce7ac
Reviewed-on: https://pdfium-review.googlesource.com/c/pdfium/+/116070
Reviewed-by: Thomas Sepez <tsepez@google.com>
Reviewed-by: Lei Zhang <thestig@chromium.org>
Commit-Queue: Tom Sepez <tsepez@chromium.org>
diff --git a/core/fxcrt/BUILD.gn b/core/fxcrt/BUILD.gn
index e2d1c6e..1531a7c 100644
--- a/core/fxcrt/BUILD.gn
+++ b/core/fxcrt/BUILD.gn
@@ -143,6 +143,7 @@
   configs += [
     "../../:pdfium_strict_config",
     "../../:pdfium_noshorten_config",
+    "../../:pdfium_nounsafe_buffer_usage_config",
   ]
   public_deps = [
     "../../:freetype_common",
diff --git a/core/fxcrt/fx_extension.cpp b/core/fxcrt/fx_extension.cpp
index 0f6b720..77afea1 100644
--- a/core/fxcrt/fx_extension.cpp
+++ b/core/fxcrt/fx_extension.cpp
@@ -29,88 +29,98 @@
 }  // namespace
 
 float FXSYS_wcstof(const wchar_t* pwsStr, size_t nLength, size_t* pUsedLen) {
-  DCHECK(pwsStr);
-  if (nLength == 0)
-    return 0.0f;
+  // SAFETY: TODO(tsepez): This is an enormous unsafe block, pretty hard to
+  // explain its soundness.
+  UNSAFE_BUFFERS({
+    DCHECK(pwsStr);
+    if (nLength == 0) {
+      return 0.0f;
+    }
 
-  size_t nUsedLen = 0;
-  bool bNegtive = false;
-  switch (pwsStr[nUsedLen]) {
-    case '-':
-      bNegtive = true;
-      [[fallthrough]];
-    case '+':
-      nUsedLen++;
-      break;
-  }
-
-  float fValue = 0.0f;
-  while (nUsedLen < nLength) {
-    wchar_t wch = pwsStr[nUsedLen];
-    if (!FXSYS_IsDecimalDigit(wch))
-      break;
-
-    fValue = fValue * 10.0f + (wch - L'0');
-    nUsedLen++;
-  }
-
-  if (nUsedLen < nLength && pwsStr[nUsedLen] == L'.') {
-    float fPrecise = 0.1f;
-    while (++nUsedLen < nLength) {
-      wchar_t wch = pwsStr[nUsedLen];
-      if (!FXSYS_IsDecimalDigit(wch))
+    size_t nUsedLen = 0;
+    bool bNegtive = false;
+    switch (pwsStr[nUsedLen]) {
+      case '-':
+        bNegtive = true;
+        [[fallthrough]];
+      case '+':
+        nUsedLen++;
         break;
-
-      fValue += (wch - L'0') * fPrecise;
-      fPrecise *= 0.1f;
-    }
-  }
-
-  if (nUsedLen < nLength &&
-      (pwsStr[nUsedLen] == 'e' || pwsStr[nUsedLen] == 'E')) {
-    ++nUsedLen;
-
-    bool negative_exponent = false;
-    if (nUsedLen < nLength &&
-        (pwsStr[nUsedLen] == '-' || pwsStr[nUsedLen] == '+')) {
-      negative_exponent = pwsStr[nUsedLen] == '-';
-      ++nUsedLen;
     }
 
-    int32_t exp_value = 0;
+    float fValue = 0.0f;
     while (nUsedLen < nLength) {
       wchar_t wch = pwsStr[nUsedLen];
       if (!FXSYS_IsDecimalDigit(wch))
         break;
 
-      exp_value = exp_value * 10.0f + (wch - L'0');
-      // Exponent is outside the valid range, fail.
-      if ((negative_exponent &&
-           -exp_value < std::numeric_limits<float>::min_exponent10) ||
-          (!negative_exponent &&
-           exp_value > std::numeric_limits<float>::max_exponent10)) {
-        if (pUsedLen)
-          *pUsedLen = 0;
-        return 0.0f;
-      }
+      fValue = fValue * 10.0f + (wch - L'0');
+      nUsedLen++;
+    }
 
+    if (nUsedLen < nLength && pwsStr[nUsedLen] == L'.') {
+      float fPrecise = 0.1f;
+      while (++nUsedLen < nLength) {
+        wchar_t wch = pwsStr[nUsedLen];
+        if (!FXSYS_IsDecimalDigit(wch)) {
+          break;
+        }
+
+        fValue += (wch - L'0') * fPrecise;
+        fPrecise *= 0.1f;
+      }
+    }
+
+    if (nUsedLen < nLength &&
+        (pwsStr[nUsedLen] == 'e' || pwsStr[nUsedLen] == 'E')) {
       ++nUsedLen;
-    }
 
-    for (size_t i = exp_value; i > 0; --i) {
-      if (exp_value > 0) {
-        if (negative_exponent)
-          fValue /= 10;
-        else
-          fValue *= 10;
+      bool negative_exponent = false;
+      if (nUsedLen < nLength &&
+          (pwsStr[nUsedLen] == '-' || pwsStr[nUsedLen] == '+')) {
+        negative_exponent = pwsStr[nUsedLen] == '-';
+        ++nUsedLen;
+      }
+
+      int32_t exp_value = 0;
+      while (nUsedLen < nLength) {
+        wchar_t wch = pwsStr[nUsedLen];
+        if (!FXSYS_IsDecimalDigit(wch)) {
+          break;
+        }
+
+        exp_value = exp_value * 10.0f + (wch - L'0');
+        // Exponent is outside the valid range, fail.
+        if ((negative_exponent &&
+             -exp_value < std::numeric_limits<float>::min_exponent10) ||
+            (!negative_exponent &&
+             exp_value > std::numeric_limits<float>::max_exponent10)) {
+          if (pUsedLen) {
+            *pUsedLen = 0;
+          }
+          return 0.0f;
+        }
+
+        ++nUsedLen;
+      }
+
+      for (size_t i = exp_value; i > 0; --i) {
+        if (exp_value > 0) {
+          if (negative_exponent) {
+            fValue /= 10;
+          } else {
+            fValue *= 10;
+          }
+        }
       }
     }
-  }
 
-  if (pUsedLen)
-    *pUsedLen = nUsedLen;
+    if (pUsedLen) {
+      *pUsedLen = nUsedLen;
+    }
 
-  return bNegtive ? -fValue : fValue;
+    return bNegtive ? -fValue : fValue;
+  });
 }
 
 wchar_t* FXSYS_wcsncpy(wchar_t* dstStr, const wchar_t* srcStr, size_t count) {
@@ -118,9 +128,15 @@
   DCHECK(srcStr);
   DCHECK(count > 0);
 
-  for (size_t i = 0; i < count; ++i)
-    if ((dstStr[i] = srcStr[i]) == L'\0')
-      break;
+  // SAFETY: TODO(tsepez): This is UNSAFE_BUFFER_USAGE as well.
+  UNSAFE_BUFFERS({
+    for (size_t i = 0; i < count; ++i) {
+      dstStr[i] = srcStr[i];
+      if (dstStr[i] == L'\0') {
+        break;
+      }
+    }
+  });
   return dstStr;
 }
 
@@ -129,25 +145,34 @@
   DCHECK(s2);
   DCHECK(count > 0);
 
-  while (count-- > 0) {
-    wchar_t wch1 = static_cast<wchar_t>(FXSYS_towlower(*s1++));
-    wchar_t wch2 = static_cast<wchar_t>(FXSYS_towlower(*s2++));
-    if (wch1 != wch2) {
-      return wch1 > wch2 ? 1 : -1;
+  // SAFETY: TODO(tsepez): This is UNSAFE_BUFFER_USAGE as well.
+  UNSAFE_BUFFERS({
+    while (count-- > 0) {
+      wchar_t wch1 = static_cast<wchar_t>(FXSYS_towlower(*s1++));
+      wchar_t wch2 = static_cast<wchar_t>(FXSYS_towlower(*s2++));
+      if (wch1 != wch2) {
+        return wch1 > wch2 ? 1 : -1;
+      }
     }
-  }
+  });
   return 0;
 }
 
 void FXSYS_IntToTwoHexChars(uint8_t n, char* buf) {
   static const char kHex[] = "0123456789ABCDEF";
-  buf[0] = kHex[n / 16];
-  buf[1] = kHex[n % 16];
+  // SAFETY: TODO(tsepez): This is UNSAFE_BUFFER_USAGE as well.
+  UNSAFE_BUFFERS({
+    buf[0] = kHex[n / 16];
+    buf[1] = kHex[n % 16];
+  });
 }
 
 void FXSYS_IntToFourHexChars(uint16_t n, char* buf) {
-  FXSYS_IntToTwoHexChars(n / 256, buf);
-  FXSYS_IntToTwoHexChars(n % 256, buf + 2);
+  // SAFETY: TODO(tsepez): This is UNSAFE_BUFFER_USAGE as well.
+  UNSAFE_BUFFERS({
+    FXSYS_IntToTwoHexChars(n / 256, buf);
+    FXSYS_IntToTwoHexChars(n % 256, buf + 2);
+  });
 }
 
 size_t FXSYS_ToUTF16BE(uint32_t unicode, char* buf) {
@@ -159,9 +184,12 @@
     FXSYS_IntToFourHexChars(unicode, buf);
     return 4;
   }
-  pdfium::SurrogatePair surrogate_pair(unicode);
-  FXSYS_IntToFourHexChars(surrogate_pair.high(), buf);
-  FXSYS_IntToFourHexChars(surrogate_pair.low(), buf + 4);
+  // SAFETY: TODO(tsepez): This is UNSAFE_BUFFER_USAGE as well.
+  UNSAFE_BUFFERS({
+    pdfium::SurrogatePair surrogate_pair(unicode);
+    FXSYS_IntToFourHexChars(surrogate_pair.high(), buf);
+    FXSYS_IntToFourHexChars(surrogate_pair.low(), buf + 4);
+  });
   return 8;
 }
 
diff --git a/core/fxcrt/fx_memory_pa.cpp b/core/fxcrt/fx_memory_pa.cpp
index be6e2af..82df893 100644
--- a/core/fxcrt/fx_memory_pa.cpp
+++ b/core/fxcrt/fx_memory_pa.cpp
@@ -6,10 +6,14 @@
 
 #include "core/fxcrt/fx_memory.h"
 
+#include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/fx_safe_types.h"
-#include "partition_alloc/partition_alloc.h"
 
-#if !defined(PDF_USE_PARTITION_ALLOC)
+#if defined(PDF_USE_PARTITION_ALLOC)
+UNSAFE_HEADERS_BEGIN()
+#include "partition_alloc/partition_alloc.h"
+UNSAFE_HEADERS_END()
+#else
 #error "File compiled under wrong build option."
 #endif
 
diff --git a/core/fxcrt/fx_system.cpp b/core/fxcrt/fx_system.cpp
index 0286b07..4eb1864 100644
--- a/core/fxcrt/fx_system.cpp
+++ b/core/fxcrt/fx_system.cpp
@@ -11,6 +11,7 @@
 #include <limits>
 
 #include "build/build_config.h"
+#include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/fx_extension.h"
 
 namespace {
@@ -26,8 +27,13 @@
 
   // Process the sign.
   bool neg = *str == '-';
-  if (neg || *str == '+')
-    str++;
+  if (neg || *str == '+') {
+    // SAFETY: `str` points at the start of the string, which is a character or
+    // a terminating NUL. `*str` is non-NUL from the condition above, so `str`
+    // is pointing inside the string. Afterward, `str` may be pointing at the
+    // terminating NUL.
+    UNSAFE_BUFFERS(str++);
+  }
 
   IntType num = 0;
   while (*str && FXSYS_IsDecimalDigit(*str)) {
@@ -37,15 +43,17 @@
         // Return MIN when the represented number is signed type and is smaller
         // than the min value.
         return std::numeric_limits<IntType>::min();
-      } else {
-        // Return MAX when the represented number is signed type and is larger
-        // than the max value, or the number is unsigned type and out of range.
-        return std::numeric_limits<IntType>::max();
       }
+      // Return MAX when the represented number is signed type and is larger
+      // than the max value, or the number is unsigned type and out of range.
+      return std::numeric_limits<IntType>::max();
     }
-
     num = num * 10 + val;
-    str++;
+
+    // SAFETY: The loop terminates if `str` is ever pointing at the terminating
+    // NUL. `str` is only moved by one character at a time, so inside the loop
+    // `str` always points inside the string.
+    UNSAFE_BUFFERS(str++);
   }
   // When it is a negative value, -num should be returned. Since num may be of
   // unsigned type, use ~num + 1 to avoid the warning of applying unary minus
@@ -55,19 +63,20 @@
 
 template <typename T, typename UT, typename STR_T>
 STR_T FXSYS_IntToStr(T value, STR_T str, int radix) {
+  // SAFETY: TODO(tsepez): investigate safety throughout.
   if (radix < 2 || radix > 16) {
     str[0] = 0;
     return str;
   }
   if (value == 0) {
     str[0] = '0';
-    str[1] = 0;
+    UNSAFE_BUFFERS(str[1]) = 0;
     return str;
   }
   int i = 0;
   UT uvalue;
   if (value < 0) {
-    str[i++] = '-';
+    UNSAFE_BUFFERS(str[i++]) = '-';
     // Standard trick to avoid undefined behaviour when negating INT_MIN.
     uvalue = static_cast<UT>(-(value + 1)) + 1;
   } else {
@@ -80,10 +89,10 @@
     order = order / radix;
   }
   for (int d = digits - 1; d > -1; d--) {
-    str[d + i] = "0123456789abcdef"[uvalue % radix];
+    UNSAFE_BUFFERS(str[d + i] = "0123456789abcdef"[uvalue % radix]);
     uvalue /= radix;
   }
-  str[digits + i] = 0;
+  UNSAFE_BUFFERS(str[digits + i]) = 0;
   return str;
 }
 
@@ -154,10 +163,11 @@
   char* s = str;
   while (*str) {
     *str = tolower(*str);
-    str++;
+    UNSAFE_BUFFERS(str++);  // SAFETY: NUL check in while condition.
   }
   return s;
 }
+
 char* FXSYS_strupr(char* str) {
   if (!str) {
     return nullptr;
@@ -165,10 +175,11 @@
   char* s = str;
   while (*str) {
     *str = toupper(*str);
-    str++;
+    UNSAFE_BUFFERS(str++);  // SAFETY: NUL check in while condition.
   }
   return s;
 }
+
 wchar_t* FXSYS_wcslwr(wchar_t* str) {
   if (!str) {
     return nullptr;
@@ -176,10 +187,11 @@
   wchar_t* s = str;
   while (*str) {
     *str = FXSYS_towlower(*str);
-    str++;
+    UNSAFE_BUFFERS(str++);  // SAFETY: NUL check in while condition.
   }
   return s;
 }
+
 wchar_t* FXSYS_wcsupr(wchar_t* str) {
   if (!str) {
     return nullptr;
@@ -187,7 +199,7 @@
   wchar_t* s = str;
   while (*str) {
     *str = FXSYS_towupper(*str);
-    str++;
+    UNSAFE_BUFFERS(str++);  // SAFETY: NUL check in while condition.
   }
   return s;
 }
@@ -198,8 +210,12 @@
   do {
     f = toupper(*str1);
     l = toupper(*str2);
-    ++str1;
-    ++str2;
+    // SAFETY: The loop breaks when `*str1` is NUL, so `str1` is always inside
+    // its string.
+    UNSAFE_BUFFERS(++str1);
+    // SAFETY: The loop breaks when `*str1` is non-NUL but `*str2` is NUL (as
+    // checked by `f != l`), so `str2` is always inside its string.
+    UNSAFE_BUFFERS(++str2);
   } while (f && f == l);
   return f - l;
 }
@@ -210,8 +226,12 @@
   do {
     f = FXSYS_towupper(*str1);
     l = FXSYS_towupper(*str2);
-    ++str1;
-    ++str2;
+    // SAFETY: The loop breaks when `*str1` is NUL, so `str1` is always inside
+    // its string.
+    UNSAFE_BUFFERS(++str1);
+    // SAFETY: The loop breaks when `*str1` is non-NUL but `*str2` is NUL (as
+    // checked by `f != l`), so `str2` is always inside its string.
+    UNSAFE_BUFFERS(++str2);
   } while (f && f == l);
   return f - l;
 }
diff --git a/core/fxcrt/fx_unicode.cpp b/core/fxcrt/fx_unicode.cpp
index ae21d90..f992c46 100644
--- a/core/fxcrt/fx_unicode.cpp
+++ b/core/fxcrt/fx_unicode.cpp
@@ -11,6 +11,8 @@
 #include <iterator>
 
 #include "core/fxcrt/check.h"
+#include "core/fxcrt/check_op.h"
+#include "core/fxcrt/compiler_specific.h"
 
 namespace {
 
@@ -33,15 +35,16 @@
 };
 #undef CHARPROP____
 
-constexpr size_t kTextLayoutCodePropertiesSize =
-    std::size(kTextLayoutCodeProperties);
-
-static_assert(kTextLayoutCodePropertiesSize == 65536, "missing characters");
+static_assert(std::size(kTextLayoutCodeProperties) == 65536,
+              "missing characters");
 
 uint16_t GetUnicodeProperties(wchar_t wch) {
   size_t idx = static_cast<size_t>(wch);
-  if (idx < kTextLayoutCodePropertiesSize)
-    return kTextLayoutCodeProperties[idx];
+  if (idx < std::size(kTextLayoutCodeProperties)) {
+    // SAFETY: `std::size(kTextLayoutCodeProperties)` is the size of the table,
+    // so the condition above verifies `idx` is in range.
+    return UNSAFE_BUFFERS(kTextLayoutCodeProperties[idx]);
+  }
   return 0;
 }
 
@@ -66,16 +69,16 @@
 };
 #undef CHARPROP____
 
-constexpr size_t kExtendedTextLayoutCodePropertiesSize =
-    std::size(kExtendedTextLayoutCodeProperties);
-
-static_assert(kExtendedTextLayoutCodePropertiesSize == 65536,
+static_assert(std::size(kExtendedTextLayoutCodeProperties) == 65536,
               "missing characters");
 
 uint16_t GetExtendedUnicodeProperties(wchar_t wch) {
   size_t idx = static_cast<size_t>(wch);
-  if (idx < kExtendedTextLayoutCodePropertiesSize)
-    return kExtendedTextLayoutCodeProperties[idx];
+  if (idx < std::size(kExtendedTextLayoutCodeProperties)) {
+    // SAFETY: `std::size(kExtendedTextLayoutCodeProperties)` is the size of
+    // the table, so the condition above verifies `idx` is in range.
+    return UNSAFE_BUFFERS(kExtendedTextLayoutCodeProperties[idx]);
+  }
   return 0;
 }
 
@@ -125,14 +128,12 @@
     0xFF5D, 0xFF5B, 0xFF60, 0xFF5F, 0xFF63, 0xFF62,
 };
 
-constexpr size_t kFXTextLayoutBidiMirrorSize =
-    std::size(kFXTextLayoutBidiMirror);
-
 // Check that the mirror indicies in the fx_ucddata.inc table are in bounds.
 #undef CHARPROP____
-#define CHARPROP____(mirror, ct, bd, bt)                                      \
-  static_assert(mirror == kMirrorMax || mirror < kFXTextLayoutBidiMirrorSize, \
-                "Bad mirror index");
+#define CHARPROP____(mirror, ct, bd, bt)                                   \
+  static_assert(                                                           \
+      mirror == kMirrorMax || mirror < std::size(kFXTextLayoutBidiMirror), \
+      "Bad mirror index");
 #include "core/fxcrt/fx_ucddata.inc"  // NOLINT(build/include)
 #undef CHARPROP____
 
@@ -143,10 +144,13 @@
 wchar_t GetMirrorChar(wchar_t wch) {
   uint16_t prop = GetUnicodeProperties(wch);
   size_t idx = prop >> kMirrorBitPos;
-  if (idx == kMirrorMax)
+  if (idx == kMirrorMax) {
     return wch;
-  DCHECK(idx < kFXTextLayoutBidiMirrorSize);
-  return kFXTextLayoutBidiMirror[idx];
+  }
+  CHECK_LT(idx, std::size(kFXTextLayoutBidiMirror));
+  // SAFETY: `std::size(kFXTextLayoutBidiMirror)` is the size of the table, so
+  // the CHECK() above verifies `idx` is in range.
+  return UNSAFE_BUFFERS(kFXTextLayoutBidiMirror[idx]);
 }
 
 FX_BIDICLASS GetBidiClass(wchar_t wch) {
diff --git a/core/fxcrt/span.h b/core/fxcrt/span.h
index 5f7adcd..2e51cd2 100644
--- a/core/fxcrt/span.h
+++ b/core/fxcrt/span.h
@@ -17,8 +17,13 @@
 #include "core/fxcrt/check.h"
 #include "core/fxcrt/compiler_specific.h"
 
+// SAFETY: TODO(crbug.com/pdfium/2085): this entire file is to be replaced
+// with the fully annotated one that is being prepared in base/.
+
 #if defined(PDF_USE_PARTITION_ALLOC)
+UNSAFE_HEADERS_BEGIN()
 #include "partition_alloc/pointers/raw_ptr.h"
+UNSAFE_HEADERS_END()
 #else
 #include "core/fxcrt/unowned_ptr_exclusion.h"
 #endif
@@ -267,7 +272,7 @@
   const span subspan(size_t pos, size_t count = dynamic_extent) const {
     CHECK(pos <= size_);
     CHECK(count == dynamic_extent || count <= size_ - pos);
-    return span(static_cast<T*>(data_) + pos,
+    return span(UNSAFE_BUFFERS(static_cast<T*>(data_) + pos),
                 count == dynamic_extent ? size_ - pos : count);
   }
 
@@ -279,7 +284,7 @@
   // [span.elem], span element access
   T& operator[](size_t index) const noexcept {
     CHECK(index < size_);
-    return static_cast<T*>(data_)[index];
+    return UNSAFE_BUFFERS(static_cast<T*>(data_)[index]);
   }
 
   constexpr T& front() const noexcept {
@@ -289,14 +294,16 @@
 
   constexpr T& back() const noexcept {
     CHECK(!empty());
-    return *(data() + size() - 1);
+    return UNSAFE_BUFFERS(*(data() + size() - 1));
   }
 
   constexpr T* data() const noexcept { return static_cast<T*>(data_); }
 
   // [span.iter], span iterator support
   constexpr iterator begin() const noexcept { return static_cast<T*>(data_); }
-  constexpr iterator end() const noexcept { return begin() + size_; }
+  constexpr iterator end() const noexcept {
+    return UNSAFE_BUFFERS(begin() + size_);
+  }
 
   constexpr const_iterator cbegin() const noexcept { return begin(); }
   constexpr const_iterator cend() const noexcept { return end(); }
diff --git a/core/fxcrt/string_template.cpp b/core/fxcrt/string_template.cpp
index 531905b..91ec7b1 100644
--- a/core/fxcrt/string_template.cpp
+++ b/core/fxcrt/string_template.cpp
@@ -64,12 +64,7 @@
 
 template <typename T>
 size_t StringTemplate<T>::Remove(T chRemove) {
-  size_t count = 0;
-  for (const auto& ch : span()) {
-    if (ch == chRemove) {
-      count++;
-    }
-  }
+  size_t count = std::count(span().begin(), span().end(), chRemove);
   if (count == 0) {
     return 0;
   }
diff --git a/core/fxcrt/unowned_ptr.h b/core/fxcrt/unowned_ptr.h
index 8f0d897..fba0e25 100644
--- a/core/fxcrt/unowned_ptr.h
+++ b/core/fxcrt/unowned_ptr.h
@@ -39,10 +39,13 @@
 // into an unowned array is desired, which performs the same checks.
 
 #include "build/build_config.h"
+#include "core/fxcrt/compiler_specific.h"
 
 #if defined(PDF_USE_PARTITION_ALLOC)
+UNSAFE_HEADERS_BEGIN()
 #include "partition_alloc/partition_alloc_buildflags.h"
 #include "partition_alloc/pointers/raw_ptr.h"
+UNSAFE_HEADERS_END()
 
 #if !BUILDFLAG(USE_PARTITION_ALLOC)
 #error "pdf_use_partition_alloc=true requires use_partition_alloc=true"
@@ -65,7 +68,6 @@
 #include <type_traits>
 #include <utility>
 
-#include "core/fxcrt/compiler_specific.h"
 #include "core/fxcrt/unowned_ptr_exclusion.h"
 
 namespace fxcrt {
diff --git a/core/fxcrt/widestring.cpp b/core/fxcrt/widestring.cpp
index 5abfc0d..84676d5 100644
--- a/core/fxcrt/widestring.cpp
+++ b/core/fxcrt/widestring.cpp
@@ -57,192 +57,199 @@
 std::optional<size_t> GuessSizeForVSWPrintf(const wchar_t* pFormat,
                                             va_list argList) {
   size_t nMaxLen = 0;
-  for (const wchar_t* pStr = pFormat; *pStr != 0; pStr++) {
-    if (*pStr != '%' || *(pStr = pStr + 1) == '%') {
-      ++nMaxLen;
-      continue;
-    }
-    int iWidth = 0;
-    for (; *pStr != 0; pStr++) {
-      if (*pStr == '#') {
-        nMaxLen += 2;
-      } else if (*pStr == '*') {
-        iWidth = va_arg(argList, int);
-      } else if (*pStr != '-' && *pStr != '+' && *pStr != '0' && *pStr != ' ') {
-        break;
+  // SAFETY: TODO(tsepez): investigate lack of safety.
+  UNSAFE_BUFFERS({
+    for (const wchar_t* pStr = pFormat; *pStr != 0; pStr++) {
+      if (*pStr != '%' || *(pStr = pStr + 1) == '%') {
+        ++nMaxLen;
+        continue;
       }
-    }
-    if (iWidth == 0) {
-      iWidth = FXSYS_wtoi(pStr);
-      while (FXSYS_IsDecimalDigit(*pStr))
-        ++pStr;
-    }
-    if (iWidth < 0 || iWidth > 128 * 1024)
-      return std::nullopt;
-    uint32_t nWidth = static_cast<uint32_t>(iWidth);
-    int iPrecision = 0;
-    if (*pStr == '.') {
-      pStr++;
-      if (*pStr == '*') {
-        iPrecision = va_arg(argList, int);
-        pStr++;
-      } else {
-        iPrecision = FXSYS_wtoi(pStr);
+      int iWidth = 0;
+      for (; *pStr != 0; pStr++) {
+        if (*pStr == '#') {
+          nMaxLen += 2;
+        } else if (*pStr == '*') {
+          iWidth = va_arg(argList, int);
+        } else if (*pStr != '-' && *pStr != '+' && *pStr != '0' &&
+                   *pStr != ' ') {
+          break;
+        }
+      }
+      if (iWidth == 0) {
+        iWidth = FXSYS_wtoi(pStr);
         while (FXSYS_IsDecimalDigit(*pStr))
           ++pStr;
       }
-    }
-    if (iPrecision < 0 || iPrecision > 128 * 1024)
-      return std::nullopt;
-    uint32_t nPrecision = static_cast<uint32_t>(iPrecision);
-    int nModifier = 0;
-    if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') {
-      pStr += 3;
-      nModifier = FORCE_INT64;
-    } else {
-      switch (*pStr) {
-        case 'h':
-          nModifier = FORCE_ANSI;
-          pStr++;
-          break;
-        case 'l':
-          nModifier = FORCE_UNICODE;
-          pStr++;
-          break;
-        case 'F':
-        case 'N':
-        case 'L':
-          pStr++;
-          break;
+      if (iWidth < 0 || iWidth > 128 * 1024) {
+        return std::nullopt;
       }
-    }
-    size_t nItemLen = 0;
-    switch (*pStr | nModifier) {
-      case 'c':
-      case 'C':
-        nItemLen = 2;
-        va_arg(argList, int);
-        break;
-      case 'c' | FORCE_ANSI:
-      case 'C' | FORCE_ANSI:
-        nItemLen = 2;
-        va_arg(argList, int);
-        break;
-      case 'c' | FORCE_UNICODE:
-      case 'C' | FORCE_UNICODE:
-        nItemLen = 2;
-        va_arg(argList, int);
-        break;
-      case 's': {
-        const wchar_t* pstrNextArg = va_arg(argList, const wchar_t*);
-        if (pstrNextArg) {
-          nItemLen = wcslen(pstrNextArg);
-          if (nItemLen < 1) {
-            nItemLen = 1;
-          }
+      uint32_t nWidth = static_cast<uint32_t>(iWidth);
+      int iPrecision = 0;
+      if (*pStr == '.') {
+        pStr++;
+        if (*pStr == '*') {
+          iPrecision = va_arg(argList, int);
+          pStr++;
         } else {
-          nItemLen = 6;
-        }
-      } break;
-      case 'S': {
-        const char* pstrNextArg = va_arg(argList, const char*);
-        if (pstrNextArg) {
-          nItemLen = strlen(pstrNextArg);
-          if (nItemLen < 1) {
-            nItemLen = 1;
+          iPrecision = FXSYS_wtoi(pStr);
+          while (FXSYS_IsDecimalDigit(*pStr)) {
+            ++pStr;
           }
-        } else {
-          nItemLen = 6;
         }
-      } break;
-      case 's' | FORCE_ANSI:
-      case 'S' | FORCE_ANSI: {
-        const char* pstrNextArg = va_arg(argList, const char*);
-        if (pstrNextArg) {
-          nItemLen = strlen(pstrNextArg);
-          if (nItemLen < 1) {
-            nItemLen = 1;
-          }
-        } else {
-          nItemLen = 6;
-        }
-      } break;
-      case 's' | FORCE_UNICODE:
-      case 'S' | FORCE_UNICODE: {
-        const wchar_t* pstrNextArg = va_arg(argList, wchar_t*);
-        if (pstrNextArg) {
-          nItemLen = wcslen(pstrNextArg);
-          if (nItemLen < 1) {
-            nItemLen = 1;
-          }
-        } else {
-          nItemLen = 6;
-        }
-      } break;
-    }
-    if (nItemLen != 0) {
-      if (nPrecision != 0 && nItemLen > nPrecision) {
-        nItemLen = nPrecision;
       }
-      if (nItemLen < nWidth) {
-        nItemLen = nWidth;
+      if (iPrecision < 0 || iPrecision > 128 * 1024) {
+        return std::nullopt;
       }
-    } else {
-      switch (*pStr) {
-        case 'd':
-        case 'i':
-        case 'u':
-        case 'x':
-        case 'X':
-        case 'o':
-          if (nModifier & FORCE_INT64) {
-            va_arg(argList, int64_t);
+      uint32_t nPrecision = static_cast<uint32_t>(iPrecision);
+      int nModifier = 0;
+      if (*pStr == L'I' && *(pStr + 1) == L'6' && *(pStr + 2) == L'4') {
+        pStr += 3;
+        nModifier = FORCE_INT64;
+      } else {
+        switch (*pStr) {
+          case 'h':
+            nModifier = FORCE_ANSI;
+            pStr++;
+            break;
+          case 'l':
+            nModifier = FORCE_UNICODE;
+            pStr++;
+            break;
+          case 'F':
+          case 'N':
+          case 'L':
+            pStr++;
+            break;
+        }
+      }
+      size_t nItemLen = 0;
+      switch (*pStr | nModifier) {
+        case 'c':
+        case 'C':
+          nItemLen = 2;
+          va_arg(argList, int);
+          break;
+        case 'c' | FORCE_ANSI:
+        case 'C' | FORCE_ANSI:
+          nItemLen = 2;
+          va_arg(argList, int);
+          break;
+        case 'c' | FORCE_UNICODE:
+        case 'C' | FORCE_UNICODE:
+          nItemLen = 2;
+          va_arg(argList, int);
+          break;
+        case 's': {
+          const wchar_t* pstrNextArg = va_arg(argList, const wchar_t*);
+          if (pstrNextArg) {
+            nItemLen = wcslen(pstrNextArg);
+            if (nItemLen < 1) {
+              nItemLen = 1;
+            }
           } else {
-            va_arg(argList, int);
+            nItemLen = 6;
           }
-          nItemLen = 32;
-          if (nItemLen < nWidth + nPrecision) {
-            nItemLen = nWidth + nPrecision;
-          }
-          break;
-        case 'a':
-        case 'A':
-        case 'e':
-        case 'E':
-        case 'g':
-        case 'G':
-          va_arg(argList, double);
-          nItemLen = 128;
-          if (nItemLen < nWidth + nPrecision) {
-            nItemLen = nWidth + nPrecision;
-          }
-          break;
-        case 'f':
-          if (nWidth + nPrecision > 100) {
-            nItemLen = nPrecision + nWidth + 128;
+        } break;
+        case 'S': {
+          const char* pstrNextArg = va_arg(argList, const char*);
+          if (pstrNextArg) {
+            nItemLen = strlen(pstrNextArg);
+            if (nItemLen < 1) {
+              nItemLen = 1;
+            }
           } else {
-            double f;
-            char pszTemp[256];
-            f = va_arg(argList, double);
-            FXSYS_snprintf(pszTemp, sizeof(pszTemp), "%*.*f", nWidth,
-                           nPrecision + 6, f);
-            nItemLen = strlen(pszTemp);
+            nItemLen = 6;
           }
-          break;
-        case 'p':
-          va_arg(argList, void*);
-          nItemLen = 32;
-          if (nItemLen < nWidth + nPrecision) {
-            nItemLen = nWidth + nPrecision;
+        } break;
+        case 's' | FORCE_ANSI:
+        case 'S' | FORCE_ANSI: {
+          const char* pstrNextArg = va_arg(argList, const char*);
+          if (pstrNextArg) {
+            nItemLen = strlen(pstrNextArg);
+            if (nItemLen < 1) {
+              nItemLen = 1;
+            }
+          } else {
+            nItemLen = 6;
           }
-          break;
-        case 'n':
-          va_arg(argList, int*);
-          break;
+        } break;
+        case 's' | FORCE_UNICODE:
+        case 'S' | FORCE_UNICODE: {
+          const wchar_t* pstrNextArg = va_arg(argList, wchar_t*);
+          if (pstrNextArg) {
+            nItemLen = wcslen(pstrNextArg);
+            if (nItemLen < 1) {
+              nItemLen = 1;
+            }
+          } else {
+            nItemLen = 6;
+          }
+        } break;
       }
+      if (nItemLen != 0) {
+        if (nPrecision != 0 && nItemLen > nPrecision) {
+          nItemLen = nPrecision;
+        }
+        if (nItemLen < nWidth) {
+          nItemLen = nWidth;
+        }
+      } else {
+        switch (*pStr) {
+          case 'd':
+          case 'i':
+          case 'u':
+          case 'x':
+          case 'X':
+          case 'o':
+            if (nModifier & FORCE_INT64) {
+              va_arg(argList, int64_t);
+            } else {
+              va_arg(argList, int);
+            }
+            nItemLen = 32;
+            if (nItemLen < nWidth + nPrecision) {
+              nItemLen = nWidth + nPrecision;
+            }
+            break;
+          case 'a':
+          case 'A':
+          case 'e':
+          case 'E':
+          case 'g':
+          case 'G':
+            va_arg(argList, double);
+            nItemLen = 128;
+            if (nItemLen < nWidth + nPrecision) {
+              nItemLen = nWidth + nPrecision;
+            }
+            break;
+          case 'f':
+            if (nWidth + nPrecision > 100) {
+              nItemLen = nPrecision + nWidth + 128;
+            } else {
+              double f;
+              char pszTemp[256];
+              f = va_arg(argList, double);
+              FXSYS_snprintf(pszTemp, sizeof(pszTemp), "%*.*f", nWidth,
+                             nPrecision + 6, f);
+              nItemLen = strlen(pszTemp);
+            }
+            break;
+          case 'p':
+            va_arg(argList, void*);
+            nItemLen = 32;
+            if (nItemLen < nWidth + nPrecision) {
+              nItemLen = nWidth + nPrecision;
+            }
+            break;
+          case 'n':
+            va_arg(argList, int*);
+            break;
+        }
+      }
+      nMaxLen += nItemLen;
     }
-    nMaxLen += nItemLen;
-  }
+  });
   nMaxLen += 32;  // Fudge factor.
   return nMaxLen;
 }