ICU-20413 ICU4C: OOM not handled in initStaticTimeZones() in timezone.cpp.
-Use static allocated memory and placement new to avoid OOM failures.
diff --git a/icu4c/source/i18n/timezone.cpp b/icu4c/source/i18n/timezone.cpp
index ccbf467..b8fe13a 100644
--- a/icu4c/source/i18n/timezone.cpp
+++ b/icu4c/source/i18n/timezone.cpp
@@ -115,9 +115,14 @@
static icu::TimeZone* DEFAULT_ZONE = NULL;
static icu::UInitOnce gDefaultZoneInitOnce = U_INITONCE_INITIALIZER;
-static icu::TimeZone* _GMT = NULL;
-static icu::TimeZone* _UNKNOWN_ZONE = NULL;
+alignas(icu::SimpleTimeZone)
+static char gRawGMT[sizeof(icu::SimpleTimeZone)];
+
+alignas(icu::SimpleTimeZone)
+static char gRawUNKNOWN[sizeof(icu::SimpleTimeZone)];
+
static icu::UInitOnce gStaticZonesInitOnce = U_INITONCE_INITIALIZER;
+static UBool gStaticZonesInitialized = FALSE; // Whether the static zones are initialized and ready to use.
static char TZDATA_VERSION[16];
static icu::UInitOnce gTZDataVersionInitOnce = U_INITONCE_INITIALIZER;
@@ -142,11 +147,12 @@
DEFAULT_ZONE = NULL;
gDefaultZoneInitOnce.reset();
- delete _GMT;
- _GMT = NULL;
- delete _UNKNOWN_ZONE;
- _UNKNOWN_ZONE = NULL;
- gStaticZonesInitOnce.reset();
+ if (gStaticZonesInitialized) {
+ reinterpret_cast<SimpleTimeZone*>(gRawGMT)->~SimpleTimeZone();
+ reinterpret_cast<SimpleTimeZone*>(gRawUNKNOWN)->~SimpleTimeZone();
+ gStaticZonesInitialized = FALSE;
+ gStaticZonesInitOnce.reset();
+ }
uprv_memset(TZDATA_VERSION, 0, sizeof(TZDATA_VERSION));
gTZDataVersionInitOnce.reset();
@@ -304,8 +310,12 @@
// Initialize _GMT independently of other static data; it should
// be valid even if we can't load the time zone UDataMemory.
ucln_i18n_registerCleanup(UCLN_I18N_TIMEZONE, timeZone_cleanup);
- _UNKNOWN_ZONE = new SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
- _GMT = new SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
+
+ // new can't fail below, as we use placement new into staticly allocated space.
+ new(gRawGMT) SimpleTimeZone(0, UnicodeString(TRUE, GMT_ID, GMT_ID_LENGTH));
+ new(gRawUNKNOWN) SimpleTimeZone(0, UnicodeString(TRUE, UNKNOWN_ZONE_ID, UNKNOWN_ZONE_ID_LENGTH));
+
+ gStaticZonesInitialized = TRUE;
}
} // anonymous namespace
@@ -314,14 +324,14 @@
TimeZone::getUnknown()
{
umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
- return *_UNKNOWN_ZONE;
+ return *reinterpret_cast<SimpleTimeZone*>(gRawUNKNOWN);
}
const TimeZone* U_EXPORT2
TimeZone::getGMT(void)
{
umtx_initOnce(gStaticZonesInitOnce, &initStaticTimeZones);
- return _GMT;
+ return reinterpret_cast<SimpleTimeZone*>(gRawGMT);
}
// *****************************************************************************
@@ -435,11 +445,8 @@
if (result == NULL) {
U_DEBUG_TZ_MSG(("failed to load time zone with id - falling to Etc/Unknown(GMT)"));
const TimeZone& unknown = getUnknown();
- if (_UNKNOWN_ZONE == NULL) { // Cannot test (&unknown == NULL) because the
- U_DEBUG_TZ_MSG(("failed to getUnknown()")); // behavior of NULL references is undefined.
- } else {
- result = unknown.clone();
- }
+ // Unknown zone uses staticly allocated memory, so creation of it can never fail due to OOM.
+ result = unknown.clone();
}
return result;
}
@@ -506,10 +513,7 @@
// code may also fail.
if (hostZone == NULL) {
const TimeZone* temptz = TimeZone::getGMT();
- // If we can't use GMT, get out.
- if (temptz == NULL) {
- return NULL;
- }
+ // GMT zone uses staticly allocated memory, so creation of it can never fail due to OOM.
hostZone = temptz->clone();
}
diff --git a/icu4c/source/test/intltest/tztest.cpp b/icu4c/source/test/intltest/tztest.cpp
index 3ab04d2..6b7746f 100644
--- a/icu4c/source/test/intltest/tztest.cpp
+++ b/icu4c/source/test/intltest/tztest.cpp
@@ -72,6 +72,7 @@
TESTCASE_AUTO(TestGetRegion);
TESTCASE_AUTO(TestGetAvailableIDsNew);
TESTCASE_AUTO(TestGetUnknown);
+ TESTCASE_AUTO(TestGetGMT);
TESTCASE_AUTO(TestGetWindowsID);
TESTCASE_AUTO(TestGetIDForWindowsID);
TESTCASE_AUTO_END;
@@ -2420,6 +2421,15 @@
assertFalse("getUnknown() uses DST", unknown.useDaylightTime());
}
+void TimeZoneTest::TestGetGMT() {
+ const TimeZone *gmt = TimeZone::getGMT();
+ UnicodeString expectedID = UNICODE_STRING_SIMPLE("GMT");
+ UnicodeString id;
+ assertEquals("getGMT() wrong ID", expectedID, gmt->getID(id));
+ assertTrue("getGMT() wrong offset", 0 == gmt->getRawOffset());
+ assertFalse("getGMT() uses DST", gmt->useDaylightTime());
+}
+
void TimeZoneTest::TestGetWindowsID(void) {
static const struct {
const char *id;
diff --git a/icu4c/source/test/intltest/tztest.h b/icu4c/source/test/intltest/tztest.h
index 99a44fe..52feaa2 100644
--- a/icu4c/source/test/intltest/tztest.h
+++ b/icu4c/source/test/intltest/tztest.h
@@ -99,6 +99,7 @@
void TestGetRegion(void);
void TestGetUnknown();
+ void TestGetGMT();
void TestGetWindowsID(void);
void TestGetIDForWindowsID(void);