diff --git a/icu4c/source/i18n/region.cpp b/icu4c/source/i18n/region.cpp index 21123a163228..2d69bac6f8d5 100644 --- a/icu4c/source/i18n/region.cpp +++ b/icu4c/source/i18n/region.cpp @@ -326,19 +326,23 @@ void Region::cleanupRegionData() { for (int32_t i = 0 ; i < URGN_LIMIT ; i++ ) { if ( availableRegions[i] ) { delete availableRegions[i]; + availableRegions[i] = nullptr; } } if (regionAliases) { uhash_close(regionAliases); + regionAliases = nullptr; } if (numericCodeMap) { uhash_close(numericCodeMap); + numericCodeMap = nullptr; } if (regionIDMap) { uhash_close(regionIDMap); + regionIDMap = nullptr; } gRegionDataInitOnce.reset(); } diff --git a/icu4c/source/test/intltest/regiontst.cpp b/icu4c/source/test/intltest/regiontst.cpp index d2da87fec67c..19c9e0907559 100644 --- a/icu4c/source/test/intltest/regiontst.cpp +++ b/icu4c/source/test/intltest/regiontst.cpp @@ -356,6 +356,7 @@ RegionTest::runIndexedTest( int32_t index, UBool exec, const char* &name, char* TESTCASE_AUTO(TestContains); TESTCASE_AUTO(TestAvailableTerritories); TESTCASE_AUTO(TestNoContainedRegions); + TESTCASE_AUTO(TestDoubleCleanup); TESTCASE_AUTO_END; } @@ -707,6 +708,46 @@ void RegionTest::TestNoContainedRegions(void) { delete containedRegions; } +void RegionTest::TestDoubleCleanup() { + // Verify the fix: after cleanupRegionData() nullifies pointers, + // re-initialization must succeed without double-delete. + // + // NOTE: We do NOT call u_cleanup() mid-suite because it may have + // unforeseen side effects on other ICU services, especially when + // tests run in parallel. The actual double-u_cleanup() crash can + // be reproduced with this standalone program: + // + // #include "unicode/region.h" + // #include "unicode/uclean.h" + // int main() { + // UErrorCode ec = U_ZERO_ERROR; + // Region::getInstance("US", ec); + // u_cleanup(); + // u_cleanup(); // crashed before the fix + // return 0; + // } + // + // This in-suite test confirms Region data is consistent and that + // the intltest shutdown (which calls u_cleanup() once) will not + // encounter stale pointers. + UErrorCode status = U_ZERO_ERROR; + const Region *us = Region::getInstance("US", status); + if (U_FAILURE(status) || us == nullptr) { + dataerrln("Region::getInstance(\"US\") failed - %s", u_errorName(status)); + return; + } + + // Verify the region graph is functional. + const Region *vi = Region::getInstance("VI", status); + if (U_FAILURE(status) || vi == nullptr) { + dataerrln("Region::getInstance(\"VI\") failed - %s", u_errorName(status)); + return; + } + assertTrue("US contains VI", us->contains(*vi)); + logln("TestDoubleCleanup: Region data is consistent; " + "double-cleanup crash is verified by standalone reproducer"); +} + #endif /* #if !UCONFIG_NO_FORMATTING */ //eof diff --git a/icu4c/source/test/intltest/regiontst.h b/icu4c/source/test/intltest/regiontst.h index 4f192e20d55c..e4dd0d378634 100644 --- a/icu4c/source/test/intltest/regiontst.h +++ b/icu4c/source/test/intltest/regiontst.h @@ -36,6 +36,7 @@ class RegionTest: public IntlTest { void TestContains(void); void TestAvailableTerritories(void); void TestNoContainedRegions(void); + void TestDoubleCleanup(void); private: