Fix undefined behavior front() call
Cherry pick undefined behavior fix from ICU PR3752

Upstream fix https://github.com/unicode-org/icu/pull/3752

Bug: 451657601
Change-Id: I2d7bad25d0c924dd57e63dce342e5e2e08cfe224
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/deps/icu/+/7099182
Reviewed-by: Philip Jägenstedt <foolip@chromium.org>
diff --git a/README.chromium b/README.chromium
index 9e6581d..fd9ca16 100644
--- a/README.chromium
+++ b/README.chromium
@@ -261,3 +261,8 @@
     patches/unnecessary_virtual_specifier.patch
   - https://issues.chromium.org/issues/403236787
   - https://patch-diff.githubusercontent.com/raw/unicode-org/icu/pull/3489.diff
+
+12. Patch incorrect call to front().
+  - patches/uloc.patch
+  - https://g-issues.chromium.org/issues/451657601
+  - https://patch-diff.githubusercontent.com/raw/unicode-org/icu/pull/3752.diff
diff --git a/patches/uloc.patch b/patches/uloc.patch
new file mode 100644
index 0000000..000e852
--- /dev/null
+++ b/patches/uloc.patch
@@ -0,0 +1,50 @@
+diff --git a/source/common/uloc.cpp b/source/common/uloc.cpp
+index bea4827a0..877874142 100644
+--- a/source/common/uloc.cpp
++++ b/source/common/uloc.cpp
+@@ -627,7 +627,7 @@ ulocimp_getKeywords(std::string_view localeID,
+         do {
+             bool duplicate = false;
+             /* skip leading spaces */
+-            while (localeID.front() == ' ') {
++            while (!localeID.empty() && localeID.front() == ' ') {
+                 localeID.remove_prefix(1);
+             }
+             if (localeID.empty()) { /* handle trailing "; " */
+diff --git a/source/test/intltest/loctest.cpp b/source/test/intltest/loctest.cpp
+index 18887ed4f..44bedd4f8 100644
+--- a/source/test/intltest/loctest.cpp
++++ b/source/test/intltest/loctest.cpp
+@@ -265,6 +265,7 @@ void LocaleTest::runIndexedTest( int32_t index, UBool exec, const char* &name, c
+     TESTCASE_AUTO(TestBug13554);
+     TESTCASE_AUTO(TestBug20410);
+     TESTCASE_AUTO(TestBug20900);
++    TESTCASE_AUTO(TestChromiumBug451657601);
+     TESTCASE_AUTO(TestLocaleCanonicalizationFromFile);
+     TESTCASE_AUTO(TestKnownCanonicalizedListCorrect);
+     TESTCASE_AUTO(TestConstructorAcceptsBCP47);
+@@ -5818,6 +5819,12 @@ void LocaleTest::TestBug20900() {
+     }
+ }
+ 
++void LocaleTest::TestChromiumBug451657601() {
++    // This used to cause a crash in _LIBCPP_HARDENING_MODE.
++    Locale l = Locale("@x=@; ");
++    assertEquals("canonicalized", "@x=@", l.getName());
++}
++
+ U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose);
+ void LocaleTest::TestLocaleCanonicalizationFromFile()
+ {
+diff --git a/source/test/intltest/loctest.h b/source/test/intltest/loctest.h
+index b3410242e..ce6c78d05 100644
+--- a/source/test/intltest/loctest.h
++++ b/source/test/intltest/loctest.h
+@@ -125,6 +125,7 @@ public:
+     void TestBug13554();
+     void TestBug20410();
+     void TestBug20900();
++    void TestChromiumBug451657601();
+     void TestLocaleCanonicalizationFromFile();
+     void TestKnownCanonicalizedListCorrect();
+     void TestConstructorAcceptsBCP47();
diff --git a/source/common/uloc.cpp b/source/common/uloc.cpp
index bea4827..8778741 100644
--- a/source/common/uloc.cpp
+++ b/source/common/uloc.cpp
@@ -627,7 +627,7 @@
         do {
             bool duplicate = false;
             /* skip leading spaces */
-            while (localeID.front() == ' ') {
+            while (!localeID.empty() && localeID.front() == ' ') {
                 localeID.remove_prefix(1);
             }
             if (localeID.empty()) { /* handle trailing "; " */
diff --git a/source/test/intltest/loctest.cpp b/source/test/intltest/loctest.cpp
index 18887ed..44bedd4 100644
--- a/source/test/intltest/loctest.cpp
+++ b/source/test/intltest/loctest.cpp
@@ -265,6 +265,7 @@
     TESTCASE_AUTO(TestBug13554);
     TESTCASE_AUTO(TestBug20410);
     TESTCASE_AUTO(TestBug20900);
+    TESTCASE_AUTO(TestChromiumBug451657601);
     TESTCASE_AUTO(TestLocaleCanonicalizationFromFile);
     TESTCASE_AUTO(TestKnownCanonicalizedListCorrect);
     TESTCASE_AUTO(TestConstructorAcceptsBCP47);
@@ -5818,6 +5819,12 @@
     }
 }
 
+void LocaleTest::TestChromiumBug451657601() {
+    // This used to cause a crash in _LIBCPP_HARDENING_MODE.
+    Locale l = Locale("@x=@; ");
+    assertEquals("canonicalized", "@x=@", l.getName());
+}
+
 U_DEFINE_LOCAL_OPEN_POINTER(LocalStdioFilePointer, FILE, fclose);
 void LocaleTest::TestLocaleCanonicalizationFromFile()
 {
diff --git a/source/test/intltest/loctest.h b/source/test/intltest/loctest.h
index b341024..ce6c78d 100644
--- a/source/test/intltest/loctest.h
+++ b/source/test/intltest/loctest.h
@@ -125,6 +125,7 @@
     void TestBug13554();
     void TestBug20410();
     void TestBug20900();
+    void TestChromiumBug451657601();
     void TestLocaleCanonicalizationFromFile();
     void TestKnownCanonicalizedListCorrect();
     void TestConstructorAcceptsBCP47();