blob: d435381a11bb31b22bdf9705a9d3bc7cb7d59433 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#define __STDC_CONSTANT_MACROS
#include <log4cxx/logstring.h>
#include <log4cxx/helpers/simpledateformat.h>
#include "../logunit.h"
#include <log4cxx/helpers/pool.h>
#include "../insertwide.h"
#include <apr.h>
#include <apr_time.h>
using namespace log4cxx;
using namespace log4cxx::helpers;
#define LOG4CXX_TEST 1
#include <log4cxx/private/log4cxx_private.h>
#if LOG4CXX_HAS_STD_LOCALE
#include <locale>
#define MAKE_LOCALE(ptr, id) \
std::locale loco(id); \
std::locale* ptr = &loco;
#else
#define MAKE_LOCALE(ptr, id) \
std::locale* ptr = NULL;
#endif
/**
* Unit test for {@link SimpleDateFormat}.
*/
LOGUNIT_CLASS(SimpleDateFormatTestCase)
{
LOGUNIT_TEST_SUITE( SimpleDateFormatTestCase );
LOGUNIT_TEST( testNameFields );
LOGUNIT_TEST( testExtremeTimesDoNotReadOutOfBounds );
LOGUNIT_TEST_SUITE_END();
#define MICROSECONDS_PER_DAY APR_INT64_C(86400000000)
public:
/**
* The day-name, month-name and AM/PM tokens index fixed-size name tables.
* Verify that ordinary formatting of those fields is unchanged after the
* bounds-checking added to guard against out-of-range time fields.
*/
void testNameFields()
{
// 02 Jan 2004 00:00:00 GMT is a Friday.
apr_time_t jan2_2004 = MICROSECONDS_PER_DAY * 12419;
MAKE_LOCALE(localeUS, "C");
{
SimpleDateFormat fmt(LOG4CXX_STR("EEE"), localeUS);
fmt.setTimeZone(TimeZone::getGMT());
LogString actual;
fmt.format(actual, jan2_2004);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Fri")), actual);
}
{
SimpleDateFormat fmt(LOG4CXX_STR("MMM"), localeUS);
fmt.setTimeZone(TimeZone::getGMT());
LogString actual;
fmt.format(actual, jan2_2004);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("Jan")), actual);
}
{
SimpleDateFormat fmt(LOG4CXX_STR("a"), localeUS);
fmt.setTimeZone(TimeZone::getGMT());
LogString actual;
fmt.format(actual, jan2_2004);
LOGUNIT_ASSERT_EQUAL(LogString(LOG4CXX_STR("AM")), actual);
}
}
/**
* Extreme apr_time_t values can make APR's time-explosion arithmetic
* overflow and produce an out-of-range tm_wday / tm_mon / tm_hour. The
* day-name, month-name and AM/PM tokens previously indexed their fixed-size
* name tables with those unchecked values, which is an out-of-bounds
* std::vector read (a heap-buffer-overflow flagged by AddressSanitizer).
*
* This exercises the affected tokens with such values and asserts the format
* completes with a bounded result rather than reading out of bounds.
*/
void testExtremeTimesDoNotReadOutOfBounds()
{
MAKE_LOCALE(localeUS, "C");
const apr_time_t times[] =
{ (apr_time_t) APR_INT64_MAX
, (apr_time_t) APR_INT64_MIN
, (apr_time_t)(APR_INT64_MAX - 1)
, (apr_time_t) APR_INT64_C(-19503744426494601)
, (apr_time_t) APR_INT64_C(9000000000000000000)
, (apr_time_t) APR_INT64_C(-9000000000000000000)
};
const logchar* patterns[] =
{ LOG4CXX_STR("EEE")
, LOG4CXX_STR("EEEE")
, LOG4CXX_STR("MMM")
, LOG4CXX_STR("MMMM")
, LOG4CXX_STR("a")
, LOG4CXX_STR("EEEE, d MMMM yyyy a")
};
for (auto t : times)
{
for (auto p : patterns)
{
SimpleDateFormat fmt(LogString(p), localeUS);
LogString actual;
fmt.format(actual, t); // must not read out of bounds
LOGUNIT_ASSERT(actual.length() < 1000);
}
}
}
};
LOGUNIT_TEST_SUITE_REGISTRATION(SimpleDateFormatTestCase);