ICU-20413 ICU4C: OOM not handled in initStaticTimeZones() in timezone.cpp.

-Use static allocated memory and placement new to avoid OOM failures.
This commit is contained in:
Jeff Genovy 2019-02-09 17:36:35 -08:00
parent 7a0a5c7ba9
commit 9f87d2d4be
3 changed files with 35 additions and 20 deletions

View file

@ -115,9 +115,14 @@ static const int32_t UNKNOWN_ZONE_ID_LENGTH = 11;
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 @@ static UBool U_CALLCONV timeZone_cleanup(void)
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 @@ void U_CALLCONV initStaticTimeZones() {
// 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 @@ const TimeZone& U_EXPORT2
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 @@ TimeZone::createTimeZone(const UnicodeString& ID)
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 @@ TimeZone::detectHostTimeZone()
// 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();
}

View file

@ -72,6 +72,7 @@ void TimeZoneTest::runIndexedTest( int32_t index, UBool exec, const char* &name,
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 @@ void TimeZoneTest::TestGetUnknown() {
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;

View file

@ -99,6 +99,7 @@ public:
void TestGetRegion(void);
void TestGetUnknown();
void TestGetGMT();
void TestGetWindowsID(void);
void TestGetIDForWindowsID(void);