From b34dbecb751cdfbb598c2ae45682e09875283118 Mon Sep 17 00:00:00 2001 From: "Philip.Hazel" Date: Sat, 5 Dec 2015 18:17:41 +0000 Subject: [PATCH] Harden pcre2test against ridiculously large values in modifiers and command line arguments. --- ChangeLog | 3 ++ src/pcre2test.c | 77 ++++++++++++++++++++++---------------------- testdata/testinput2 | 9 ++++++ testdata/testoutput2 | 16 +++++++++ 4 files changed, 67 insertions(+), 38 deletions(-) diff --git a/ChangeLog b/ChangeLog index 02333f9..e27b96b 100644 --- a/ChangeLog +++ b/ChangeLog @@ -383,6 +383,9 @@ was found by the LLVM fuzzer. 110. Implemented PCRE2_SUBSTITUTE_UNSET_EMPTY, and updated pcre2test to make it possible to test it. +111. "Harden" pcre2test against ridiculously large values in modifiers and +command line arguments. + Version 10.20 30-June-2015 -------------------------- diff --git a/src/pcre2test.c b/src/pcre2test.c index 984c35e..93f57f7 100644 --- a/src/pcre2test.c +++ b/src/pcre2test.c @@ -2973,33 +2973,6 @@ return 0; -/************************************************* -* Read number from string * -*************************************************/ - -/* We don't use strtoul() because SunOS4 doesn't have it. Rather than mess -around with conditional compilation, just do the job by hand. It is only used -for unpicking arguments, so just keep it simple. - -Arguments: - str string to be converted - endptr where to put the end pointer - -Returns: the unsigned long -*/ - -static int -get_value(const char *str, const char **endptr) -{ -int result = 0; -while(*str != 0 && isspace(*str)) str++; -while (isdigit(*str)) result = result * 10 + (int)(*str++ - '0'); -*endptr = str; -return(result); -} - - - /************************************************* * Scan the main modifier list * *************************************************/ @@ -3149,6 +3122,8 @@ static BOOL decode_modifiers(uint8_t *p, int ctx, patctl *pctl, datctl *dctl) { uint8_t *ep, *pp; +long li; +unsigned long uli; BOOL first = TRUE; for (;;) @@ -3314,9 +3289,15 @@ for (;;) case MOD_IN2: /* One or two unsigned integers */ if (!isdigit(*pp)) goto INVALID_VALUE; - ((uint32_t *)field)[0] = (uint32_t)strtoul((const char *)pp, &endptr, 10); + uli = strtoul((const char *)pp, &endptr, 10); + if (uli > UINT32_MAX) goto INVALID_VALUE; + ((uint32_t *)field)[0] = (uint32_t)uli; if (*endptr == ':') - ((uint32_t *)field)[1] = (uint32_t)strtoul((const char *)endptr+1, &endptr, 10); + { + uli = strtoul((const char *)endptr+1, &endptr, 10); + if (uli > UINT32_MAX) goto INVALID_VALUE; + ((uint32_t *)field)[1] = (uint32_t)uli; + } else ((uint32_t *)field)[1] = 0; pp = (uint8_t *)endptr; break; @@ -3331,19 +3312,25 @@ for (;;) case MOD_SIZ: /* PCRE2_SIZE value */ if (!isdigit(*pp)) goto INVALID_VALUE; - *((PCRE2_SIZE *)field) = (PCRE2_SIZE)strtoul((const char *)pp, &endptr, 10); + uli = strtoul((const char *)pp, &endptr, 10); + if (uli == ULONG_MAX) goto INVALID_VALUE; + *((PCRE2_SIZE *)field) = (PCRE2_SIZE)uli; pp = (uint8_t *)endptr; break; case MOD_INT: /* Unsigned integer */ if (!isdigit(*pp)) goto INVALID_VALUE; - *((uint32_t *)field) = (uint32_t)strtoul((const char *)pp, &endptr, 10); + uli = strtoul((const char *)pp, &endptr, 10); + if (uli > UINT32_MAX) goto INVALID_VALUE; + *((uint32_t *)field) = (uint32_t)uli; pp = (uint8_t *)endptr; break; case MOD_INS: /* Signed integer */ if (!isdigit(*pp) && *pp != '-') goto INVALID_VALUE; - *((int32_t *)field) = (int32_t)strtol((const char *)pp, &endptr, 10); + li = strtol((const char *)pp, &endptr, 10); + if (li > INT32_MAX || li < INT32_MIN) goto INVALID_VALUE; + *((int32_t *)field) = (int32_t)li; pp = (uint8_t *)endptr; break; @@ -3371,7 +3358,10 @@ for (;;) if (isdigit(*pp) || *pp == '-') { int ct = MAXCPYGET - 1; - int32_t value = (int32_t)strtol((const char *)pp, &endptr, 10); + int32_t value; + li = strtol((const char *)pp, &endptr, 10); + if (li > INT32_MAX || li < INT32_MIN) goto INVALID_VALUE; + value = (int32_t)li; field = (char *)field - m->offset + m->value; /* Adjust field ptr */ if (value >= 0) /* Add new number */ { @@ -6894,8 +6884,9 @@ def_datctl.cfail[0] = def_datctl.cfail[1] = CFAIL_UNSET; while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) { - const char *endptr; + char *endptr; char *arg = argv[op]; + unsigned long uli; /* Display and/or set return code for configuration options. */ @@ -6945,7 +6936,7 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) /* Set system stack size */ else if (strcmp(arg, "-S") == 0 && argc > 2 && - ((stack_size = get_value(argv[op+1], &endptr)), *endptr == 0)) + ((uli = strtoul(argv[op+1], &endptr, 10)), *endptr == 0)) { #if defined(_WIN32) || defined(WIN32) || defined(__minix) || defined(NATIVE_ZOS) || defined(__VMS) fprintf(stderr, "pcre2test: -S is not supported on this OS\n"); @@ -6953,6 +6944,12 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) #else int rc; struct rlimit rlim; + if (uli > UINT32_MAX) + { + fprintf(stderr, "+++ Argument for -S is too big\n"); + exit(1); + } + stack_size = (uint32_t)uli; getrlimit(RLIMIT_STACK, &rlim); rlim.rlim_cur = stack_size * 1024 * 1024; if (rlim.rlim_cur > rlim.rlim_max) @@ -6995,12 +6992,16 @@ while (argc > 1 && argv[op][0] == '-' && argv[op][1] != 0) else if (strcmp(arg, "-t") == 0 || strcmp(arg, "-tm") == 0 || strcmp(arg, "-T") == 0 || strcmp(arg, "-TM") == 0) { - int temp; int both = arg[2] == 0; showtotaltimes = arg[1] == 'T'; - if (argc > 2 && (temp = get_value(argv[op+1], &endptr), *endptr == 0)) + if (argc > 2 && (uli = strtoul(argv[op+1], &endptr, 10), *endptr == 0)) { - timeitm = temp; + if (uli > INT32_MAX) + { + fprintf(stderr, "+++ Argument for %s is too big\n", arg); + exit(1); + } + timeitm = (int)uli; op++; argc--; } diff --git a/testdata/testinput2 b/testdata/testinput2 index cbb2dcb..075d414 100644 --- a/testdata/testinput2 +++ b/testdata/testinput2 @@ -4756,4 +4756,13 @@ a)"xI /a|(b)c/replace=>$2<,substitute_unset_empty cat +/()()()/use_offset_limit + \=ovector=11000000000 + \=callout_fail=11000000000 + \=callout_fail=1:11000000000 + \=callout_data=11000000000 + \=callout_data=-11000000000 + \=offset_limit=1100000000000000000000 + \=copy=11000000000 + # End of testinput2 diff --git a/testdata/testoutput2 b/testdata/testoutput2 index 023843b..ea0c69d 100644 --- a/testdata/testoutput2 +++ b/testdata/testoutput2 @@ -15085,4 +15085,20 @@ Failed: error -55 at offset 3 in replacement: requested value is not set cat Failed: error -49 at offset 3 in replacement: unknown substring +/()()()/use_offset_limit + \=ovector=11000000000 +** Invalid value in 'ovector=11000000000' + \=callout_fail=11000000000 +** Invalid value in 'callout_fail=11000000000' + \=callout_fail=1:11000000000 +** Invalid value in 'callout_fail=1:11000000000' + \=callout_data=11000000000 +** Invalid value in 'callout_data=11000000000' + \=callout_data=-11000000000 +** Invalid value in 'callout_data=-11000000000' + \=offset_limit=1100000000000000000000 +** Invalid value in 'offset_limit=1100000000000000000000' + \=copy=11000000000 +** Invalid value in 'copy=11000000000' + # End of testinput2