CVE-2016-1669
20 Sep 2016 | categories noteshttps://bugs.chromium.org/p/chromium/issues/detail?id=606115
test.js(不一定能复现)
r2 = new RegExp("(?=)*", "g");
s0 = Array(220000700).join('a'); // the size could be different between v8 and chrome
result = s0.match(r2);
原因
这个漏洞非常有意思,其实跟正则没什么管理,而是内存管理的细节没有考虑到位,导致溢出。
// runtime/runtime-strings.cc
334 RUNTIME_FUNCTION(Runtime_StringMatch) {
..
349 ZoneScope zone_scope(isolate->runtime_zone());
350 ZoneList<int> offsets(8, zone_scope.zone());
351
352 while (true) {
353 int32_t* match = global_cache.FetchNext();
354 if (match == NULL) break;
355 offsets.Add(match[0], zone_scope.zone()); // start <----------
356 offsets.Add(match[1], zone_scope.zone()); // end
357 }
这里进行了非常多次的Add
操作,导致 offsets 这个 ZoneList 变得非常大
#0 v8::internal::Zone::New (this=0x42d61b8, size=18432) at ../src/zone.cc:112
#1 0x0000000000d1c225 in v8::internal::ZoneAllocationPolicy::New (
this=0x7ffc861b73f8, size=18428) at .././src/zone.h:160
#2 0x0000000000f5947a in v8::internal::List<int, v8::internal::ZoneAllocationPolicy>::NewData (this=0x7ffc861b7648, n=4607, allocator=...)
at .././src/list.h:182
#3 0x0000000000f593fc in v8::internal::List<int, v8::internal::ZoneAllocationPolicy>::Resize (this=0x7ffc861b7648, new_capacity=4607, alloc=...)
at .././src/list-inl.h:71
#4 0x0000000000f5933f in v8::internal::List<int, v8::internal::ZoneAllocationPolicy>::ResizeAddInternal (this=0x7ffc861b7648, element=@0x42d9f5c: 1151,
alloc=...) at .././src/list-inl.h:63
#5 0x0000000000f5928d in v8::internal::List<int, v8::internal::ZoneAllocationPolicy>::ResizeAdd (this=0x7ffc861b7648, element=@0x42d9f5c: 1151, alloc=...)
at .././src/list-inl.h:50
#6 0x0000000000f59255 in v8::internal::List<int, v8::internal::ZoneAllocationPolicy>::Add (this=0x7ffc861b7648, element=@0x42d9f5c: 1151, alloc=...)
at .././src/list-inl.h:22
#7 0x0000000000f591e0 in v8::internal::ZoneList<int>::Add (
this=0x7ffc861b7648, element=@0x42d9f5c: 1151, zone=0x42d61b8)
at .././src/zone.h:193
在这个过程中,下面的 position_ 和 limit_ 都会逐渐变大,但是有可能 position_ + size_with_redzone 先溢出
// ../src/zone.cc
void* Zone::New(size_t size) {
// Round up the requested size to fit the alignment.
size = RoundUp(size, kAlignment);
// If the allocation size is divisible by 8 then we return an 8-byte aligned
// address.
if (kPointerSize == 4 && kAlignment == 4) {
position_ += ((~size) & 4) & (reinterpret_cast<intptr_t>(position_) & 4);
} else {
DCHECK(kAlignment >= kPointerSize);
}
// Check if the requested size is available without expanding.
Address result = position_;
const size_t size_with_redzone = size + kASanRedzoneBytes;
if (limit_ < position_ + size_with_redzone) { // 会溢出,导致IF语句为FALSE
result = NewExpand(size_with_redzone);
} else {
position_ += size_with_redzone; // position_ 实际变小了
}