diff --git a/base/json/string_escape.cc b/base/json/string_escape.cc
index 1e90304646401..ac482da8c463f 100644
--- a/base/json/string_escape.cc
+++ b/base/json/string_escape.cc
@@ -90,7 +90,7 @@ bool EscapeJSONStringImpl(const S& str, bool put_in_quotes, std::string* dest) {
   const size_t length = str.length();
   for (size_t i = 0; i < length; ++i) {
     base_icu::UChar32 code_point;
-    if (!ReadUnicodeCharacter(str.data(), length, &i, &code_point) ||
+    if (!ReadUnicodeCharacter(base::span(str), &i, &code_point) ||
         code_point == CBU_SENTINEL) {
       code_point = kReplacementCodePoint;
       did_replacement = true;
diff --git a/base/strings/escape.cc b/base/strings/escape.cc
index c435392dc835c..e2c86ec6a74be 100644
--- a/base/strings/escape.cc
+++ b/base/strings/escape.cc
@@ -234,7 +234,8 @@ bool UnescapeUTF8CharacterAtIndex(std::string_view escaped_text,
 
   size_t char_index = 0;
   // Check if the unicode "character" that was just unescaped is valid.
-  if (!ReadUnicodeCharacter(reinterpret_cast<char*>(bytes), num_bytes,
+  if (!ReadUnicodeCharacter(UNSAFE_BUFFERS(base::span(
+                                reinterpret_cast<const char*>(bytes), num_bytes)),
                             &char_index, code_point_out)) {
     return false;
   }
diff --git a/base/strings/utf_offset_string_conversions.cc b/base/strings/utf_offset_string_conversions.cc
index 140a34ec3791e..90daa12de5665 100644
--- a/base/strings/utf_offset_string_conversions.cc
+++ b/base/strings/utf_offset_string_conversions.cc
@@ -194,19 +194,18 @@ void OffsetAdjuster::MergeSequentialAdjustments(
 // alterations to the string that are not one-character-to-one-character.
 // It will always be sorted by increasing offset.
 template <typename SrcChar, typename DestStdString>
-bool ConvertUnicode(const SrcChar* src,
-                    size_t src_len,
+bool ConvertUnicode(base::span<const SrcChar> src,
                     DestStdString* output,
                     OffsetAdjuster::Adjustments* adjustments) {
   if (adjustments) {
     adjustments->clear();
   }
   bool success = true;
-  for (size_t i = 0; i < src_len; i++) {
+  for (size_t i = 0; i < src.size(); i++) {
     base_icu::UChar32 code_point;
     size_t original_i = i;
     size_t chars_written = 0;
-    if (ReadUnicodeCharacter(src, src_len, &i, &code_point)) {
+    if (ReadUnicodeCharacter(src, &i, &code_point)) {
       chars_written = WriteUnicodeCharacter(code_point, output);
     } else {
       chars_written = WriteUnicodeCharacter(0xFFFD, output);
@@ -232,7 +231,8 @@ bool UTF8ToUTF16WithAdjustments(
     std::u16string* output,
     base::OffsetAdjuster::Adjustments* adjustments) {
   PrepareForUTF16Or32Output(src, src_len, output);
-  return ConvertUnicode(src, src_len, output, adjustments);
+  return ConvertUnicode(UNSAFE_BUFFERS(base::span(src, src_len)), output,
+                        adjustments);
 }
 
 std::u16string UTF8ToUTF16WithAdjustments(
@@ -266,9 +266,9 @@ std::string UTF16ToUTF8AndAdjustOffsets(
     }
   }
   std::string result;
-  PrepareForUTF8Output(utf16.data(), utf16.length(), &result);
+  PrepareForUTF8Output(base::span(utf16), &result);
   OffsetAdjuster::Adjustments adjustments;
-  ConvertUnicode(utf16.data(), utf16.length(), &result, &adjustments);
+  ConvertUnicode(base::span(utf16), &result, &adjustments);
   OffsetAdjuster::AdjustOffsets(adjustments, offsets_for_adjustment);
   return result;
 }
diff --git a/base/strings/utf_string_conversion_utils.cc b/base/strings/utf_string_conversion_utils.cc
index e905b8d62e324..b91e70459ea48 100644
--- a/base/strings/utf_string_conversion_utils.cc
+++ b/base/strings/utf_string_conversion_utils.cc
@@ -2,10 +2,7 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#ifdef UNSAFE_BUFFERS_BUILD
-// TODO(crbug.com/40284755): Remove this and spanify to fix the errors.
-#pragma allow_unsafe_buffers
-#endif
+#include "base/containers/span.h"
 
 #include "base/strings/utf_string_conversion_utils.h"
 
@@ -22,7 +19,10 @@ std::optional<size_t> CountUnicodeCharacters(std::string_view text,
   size_t count = 0;
   for (size_t index = 0; count < limit && index < text.size();
        ++count, ++index) {
-    if (!ReadUnicodeCharacter(text.data(), text.size(), &index, &unused)) {
+    if (!ReadUnicodeCharacter(
+            UNSAFE_BUFFERS(base::span(
+                reinterpret_cast<const char*>(text.data()), text.size())),
+            &index, &unused)) {
       return std::nullopt;
     }
   }
@@ -31,13 +31,12 @@ std::optional<size_t> CountUnicodeCharacters(std::string_view text,
 
 // ReadUnicodeCharacter --------------------------------------------------------
 
-bool ReadUnicodeCharacter(const char* src,
-                          size_t src_len,
+bool ReadUnicodeCharacter(base::span<const char> src,
                           size_t* char_index,
                           base_icu::UChar32* code_point_out) {
   base_icu::UChar32 code_point;
-  CBU8_NEXT(reinterpret_cast<const uint8_t*>(src), *char_index, src_len,
-            code_point);
+  UNSAFE_BUFFERS(CBU8_NEXT(reinterpret_cast<const uint8_t*>(src.data()),
+                           *char_index, src.size(), code_point));
   *code_point_out = code_point;
 
   // The ICU macro above moves to the next char, we want to point to the last
@@ -48,36 +47,36 @@ bool ReadUnicodeCharacter(const char* src,
   return IsValidCodepoint(code_point);
 }
 
-bool ReadUnicodeCharacter(const char16_t* src,
-                          size_t src_len,
+bool ReadUnicodeCharacter(base::span<const char16_t> src,
                           size_t* char_index,
                           base_icu::UChar32* code_point) {
-  if (CBU16_IS_SURROGATE(src[*char_index])) {
-    if (!CBU16_IS_SURROGATE_LEAD(src[*char_index]) || !src_len ||
-        *char_index >= src_len - 1 || !CBU16_IS_TRAIL(src[*char_index + 1])) {
+  if (UNSAFE_BUFFERS(CBU16_IS_SURROGATE(src[*char_index]))) {
+    if (UNSAFE_BUFFERS(!CBU16_IS_SURROGATE_LEAD(src[*char_index])) ||
+        !src.size() || *char_index >= src.size() - 1 ||
+        UNSAFE_BUFFERS(!CBU16_IS_TRAIL(src[*char_index + 1]))) {
       // Invalid surrogate pair.
       return false;
     }
 
     // Valid surrogate pair.
-    *code_point =
-        CBU16_GET_SUPPLEMENTARY(src[*char_index], src[*char_index + 1]);
+    *code_point = UNSAFE_BUFFERS(
+        CBU16_GET_SUPPLEMENTARY(src[*char_index], src[*char_index + 1]));
     (*char_index)++;
   } else {
     // Not a surrogate, just one 16-bit word.
-    *code_point = src[*char_index];
+    *code_point = UNSAFE_BUFFERS(src[*char_index]);
   }
 
   return IsValidCodepoint(*code_point);
 }
 
 #if defined(WCHAR_T_IS_32_BIT)
-bool ReadUnicodeCharacter(const wchar_t* src,
-                          size_t src_len,
+bool ReadUnicodeCharacter(base::span<const wchar_t> src,
                           size_t* char_index,
                           base_icu::UChar32* code_point) {
   // Conversion is easy since the source is 32-bit.
-  *code_point = static_cast<base_icu::UChar32>(src[*char_index]);
+  *code_point =
+      static_cast<base_icu::UChar32>(UNSAFE_BUFFERS(src[*char_index]));
 
   // Validate the value.
   return IsValidCodepoint(*code_point);
@@ -99,8 +98,8 @@ size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
   size_t original_char_offset = char_offset;
   output->resize(char_offset + CBU8_MAX_LENGTH);
 
-  CBU8_APPEND_UNSAFE(reinterpret_cast<uint8_t*>(output->data()), char_offset,
-                     code_point);
+  UNSAFE_BUFFERS(CBU8_APPEND_UNSAFE(
+      reinterpret_cast<uint8_t*>(output->data()), char_offset, code_point));
 
   // CBU8_APPEND_UNSAFE will advance our pointer past the inserted character, so
   // it will represent the new length of the string.
@@ -118,35 +117,33 @@ size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
   // Non-BMP characters use a double-character encoding.
   size_t char_offset = output->length();
   output->resize(char_offset + CBU16_MAX_LENGTH);
-  CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point);
+  UNSAFE_BUFFERS(CBU16_APPEND_UNSAFE(&(*output)[0], char_offset, code_point));
   return CBU16_MAX_LENGTH;
 }
 
 // Generalized Unicode converter -----------------------------------------------
 
 template <typename CHAR>
-void PrepareForUTF8Output(const CHAR* src,
-                          size_t src_len,
-                          std::string* output) {
+void PrepareForUTF8Output(base::span<const CHAR> src, std::string* output) {
   output->clear();
-  if (src_len == 0) {
+  if (src.empty()) {
     return;
   }
-  if (src[0] < 0x80) {
+  if (UNSAFE_BUFFERS(src[0] < 0x80)) {
     // Assume that the entire input will be ASCII.
-    output->reserve(src_len);
+    output->reserve(src.size());
   } else {
     // Assume that the entire input is non-ASCII and will have 3 bytes per char.
-    output->reserve(src_len * 3);
+    output->reserve(src.size() * 3);
   }
 }
 
 // Instantiate versions we know callers will need.
 #if !BUILDFLAG(IS_WIN)
 // wchar_t and char16_t are the same thing on Windows.
-template void PrepareForUTF8Output(const wchar_t*, size_t, std::string*);
+template void PrepareForUTF8Output(base::span<const wchar_t>, std::string*);
 #endif
-template void PrepareForUTF8Output(const char16_t*, size_t, std::string*);
+template void PrepareForUTF8Output(base::span<const char16_t>, std::string*);
 
 template <typename STRING>
 void PrepareForUTF16Or32Output(const char* src,
@@ -156,7 +153,7 @@ void PrepareForUTF16Or32Output(const char* src,
   if (src_len == 0) {
     return;
   }
-  if (static_cast<unsigned char>(src[0]) < 0x80) {
+  if (static_cast<unsigned char>(UNSAFE_BUFFERS(src[0])) < 0x80) {
     // Assume the input is all ASCII, which means 1:1 correspondence.
     output->reserve(src_len);
   } else {
@@ -173,4 +170,4 @@ template void PrepareForUTF16Or32Output(const char*, size_t, std::wstring*);
 #endif
 template void PrepareForUTF16Or32Output(const char*, size_t, std::u16string*);
 
-}  // namespace base
+}  // namespace base
\ No newline at end of file
diff --git a/base/strings/utf_string_conversion_utils.h b/base/strings/utf_string_conversion_utils.h
index a730215a9d03c..e8d6ac944b7e9 100644
--- a/base/strings/utf_string_conversion_utils.h
+++ b/base/strings/utf_string_conversion_utils.h
@@ -16,6 +16,7 @@
 #include <string>
 #include <string_view>
 
+#include "base/containers/span.h"
 #include "base/base_export.h"
 #include "base/third_party/icu/icu_utf.h"
 #include "build/build_config.h"
@@ -59,21 +60,18 @@ BASE_EXPORT std::optional<size_t> CountUnicodeCharacters(
 // (as in a for loop) will take the reader to the next character.
 //
 // Returns true on success. On false, |*code_point| will be invalid.
-BASE_EXPORT bool ReadUnicodeCharacter(const char* src,
-                                      size_t src_len,
+BASE_EXPORT bool ReadUnicodeCharacter(base::span<const char> src,
                                       size_t* char_index,
                                       base_icu::UChar32* code_point_out);
 
 // Reads a UTF-16 character. The usage is the same as the 8-bit version above.
-BASE_EXPORT bool ReadUnicodeCharacter(const char16_t* src,
-                                      size_t src_len,
+BASE_EXPORT bool ReadUnicodeCharacter(base::span<const char16_t> src,
                                       size_t* char_index,
                                       base_icu::UChar32* code_point);
 
 #if defined(WCHAR_T_IS_32_BIT)
 // Reads UTF-32 character. The usage is the same as the 8-bit version above.
-BASE_EXPORT bool ReadUnicodeCharacter(const wchar_t* src,
-                                      size_t src_len,
+BASE_EXPORT bool ReadUnicodeCharacter(base::span<const wchar_t> src,
                                       size_t* char_index,
                                       base_icu::UChar32* code_point);
 #endif  // defined(WCHAR_T_IS_32_BIT)
@@ -108,7 +106,7 @@ inline size_t WriteUnicodeCharacter(base_icu::UChar32 code_point,
 // character types are unsigned, which will be true for UTF-16 and -32 on our
 // systems.
 template <typename CHAR>
-void PrepareForUTF8Output(const CHAR* src, size_t src_len, std::string* output);
+void PrepareForUTF8Output(base::span<const CHAR> src, std::string* output);
 
 // Prepares an output buffer (containing either UTF-16 or -32 data) given some
 // UTF-8 input that will be converted to it.  See PrepareForUTF8Output().
