diff --git a/src/flb_parser.c b/src/flb_parser.c index c4b6b2d6036..2f7c8fec9ed 100644 --- a/src/flb_parser.c +++ b/src/flb_parser.c @@ -1165,18 +1165,38 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, time_t time_now; char *p = NULL; char *fmt; - int time_len = tsize; + char *buf; + char *time_buf = NULL; + int time_len; const char *time_ptr = time_str; char tmp[64]; + size_t buf_size = sizeof(tmp); struct tm tmy; *ns = 0; - if (tsize > sizeof(tmp) - 1) { - flb_error("[parser] time string length is too long"); + if (tsize > INT_MAX) { + flb_error("[parser] time string length exceeds supported range"); return -1; } + time_len = (int) tsize; + buf = tmp; + + if (tsize > sizeof(tmp) - 1) { + if (tsize > (SIZE_MAX - 8)) { + flb_error("[parser] time string length is too long"); + return -1; + } + buf_size = tsize + 8; + time_buf = flb_malloc(buf_size); + if (time_buf == NULL) { + flb_errno(); + return -1; + } + buf = time_buf; + } + /* * Some records coming from old Syslog messages do not contain the * year, so it's required to ingest this information in the value @@ -1184,7 +1204,8 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, */ if (parser->time_with_year == FLB_FALSE) { /* Given time string is too long */ - if (time_len + 6 >= sizeof(tmp)) { + if (time_len + 6 >= buf_size) { + flb_free(time_buf); return -1; } @@ -1212,7 +1233,7 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, uint64_t t = tmy.tm_year + 1900; - fmt = tmp; + fmt = buf; u64_to_str(t, fmt); fmt += 4; *fmt++ = ' '; @@ -1221,8 +1242,8 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, fmt += time_len; *fmt++ = '\0'; - time_ptr = tmp; - time_len = strlen(tmp); + time_ptr = buf; + time_len = strlen(buf); p = flb_strptime(time_ptr, parser->time_fmt_year, tm); } else { @@ -1231,13 +1252,14 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, * null-terminated, which time_ptr is not guaranteed * to be. So we use tmp to hold our string. */ - if (time_len >= sizeof(tmp)) { + if (time_len >= buf_size) { + flb_free(time_buf); return -1; } - memcpy(tmp, time_ptr, time_len); - tmp[time_len] = '\0'; - time_ptr = tmp; - time_len = strlen(tmp); + memcpy(buf, time_ptr, time_len); + buf[time_len] = '\0'; + time_ptr = buf; + time_len = strlen(buf); p = flb_strptime(time_ptr, parser->time_fmt, tm); } @@ -1245,9 +1267,11 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, if (p == NULL) { if (parser->time_strict) { flb_error("[parser] cannot parse '%.*s'", (int)tsize, time_str); + flb_free(time_buf); return -1; } flb_debug("[parser] non-exact match '%.*s'", (int)tsize, time_str); + flb_free(time_buf); return 0; } @@ -1256,9 +1280,11 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, if (ret < 0) { if (parser->time_strict) { flb_error("[parser] cannot parse %%L for '%.*s'", (int)tsize, time_str); + flb_free(time_buf); return -1; } flb_debug("[parser] non-exact match on %%L '%.*s'", (int)tsize, time_str); + flb_free(time_buf); return 0; } p += ret; @@ -1268,9 +1294,11 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, if (p == NULL) { if (parser->time_strict) { flb_error("[parser] cannot parse '%.*s' after %%L", (int)tsize, time_str); + flb_free(time_buf); return -1; } flb_debug("[parser] non-exact match after %%L '%.*s'", (int)tsize, time_str); + flb_free(time_buf); return 0; } } @@ -1279,6 +1307,7 @@ int flb_parser_time_lookup(const char *time_str, size_t tsize, flb_tm_gmtoff(tm) = parser->time_offset; } + flb_free(time_buf); return 0; } diff --git a/tests/internal/parser.c b/tests/internal/parser.c index 8337e4f89fd..cc5236eb4e5 100644 --- a/tests/internal/parser.c +++ b/tests/internal/parser.c @@ -9,6 +9,9 @@ #include #include +#include +#include +#include #include "flb_tests_internal.h" /* Parsers configuration */ @@ -588,6 +591,64 @@ void test_parser_time_system_timezone_midnight() flb_config_exit(config); } +void test_parser_time_lookup_long_fraction() +{ + int ret; + double ns; + time_t epoch; + struct flb_tm tm; + struct flb_parser *parser; + struct flb_config *config; + const char *time_string = "10/03/2016 12:21:08.123456789123456789123456789123456789123456789123456789"; + + config = flb_config_init(); + load_json_parsers(config); + + parser = flb_parser_get("generic_N", config); + TEST_CHECK(parser != NULL); + if (parser == NULL) { + flb_parser_exit(config); + flb_config_exit(config); + return; + } + + ret = flb_parser_time_lookup(time_string, strlen(time_string), 0, parser, &tm, &ns); + TEST_CHECK(ret == 0); + + epoch = flb_parser_tm2time(&tm, FLB_FALSE); + TEST_CHECK(epoch == 1475497268); + TEST_CHECK(fabs(ns - 0.123456789) <= DBL_EPSILON); + + flb_parser_exit(config); + flb_config_exit(config); +} + +void test_parser_time_lookup_reject_oversized_length() +{ + int ret; + double ns; + struct flb_tm tm; + struct flb_parser *parser; + struct flb_config *config; + + config = flb_config_init(); + load_json_parsers(config); + + parser = flb_parser_get("generic_N", config); + TEST_CHECK(parser != NULL); + if (parser == NULL) { + flb_parser_exit(config); + flb_config_exit(config); + return; + } + + ret = flb_parser_time_lookup("x", (size_t) INT_MAX + 1, 0, parser, &tm, &ns); + TEST_CHECK(ret == -1); + + flb_parser_exit(config); + flb_config_exit(config); +} + TEST_LIST = { { "tzone_offset", test_parser_tzone_offset}, @@ -595,6 +656,8 @@ TEST_LIST = { { "json_time_lookup", test_json_parser_time_lookup}, { "regex_time_lookup", test_regex_parser_time_lookup}, { "time_system_timezone_midnight", test_parser_time_system_timezone_midnight}, + { "time_lookup_long_fraction", test_parser_time_lookup_long_fraction}, + { "time_lookup_reject_oversized_length", test_parser_time_lookup_reject_oversized_length}, { "mysql_unquoted" , test_mysql_unquoted }, { 0 } };