There's an integer overflow in computing the required allocation size when instantiating a new javascript object.
See the following code in objects.cc
// static
bool JSFunction::CalculateInstanceSizeForDerivedClass(
Handle<JSFunction> function, InstanceType instance_type,
int requested_embedder_fields, int* instance_size,
int* in_object_properties) {
Isolate* isolate = function->GetIsolate();
int expected_nof_properties = 0;
bool result = true;
for (PrototypeIterator iter(isolate, function, kStartAtReceiver);
!iter.IsAtEnd(); iter.Advance()) {
Handle<JSReceiver> current =
PrototypeIterator::GetCurrent<JSReceiver>(iter);
if (!current->IsJSFunction()) break;
Handle<JSFunction> func(Handle<JSFunction>::cast(current));
// The super constructor should be compiled for the number of expected
// properties to be available.
Handle<SharedFunctionInfo> shared(func->shared());
if (shared->is_compiled() ||
Compiler::Compile(func, Compiler::CLEAR_EXCEPTION)) {
DCHECK(shared->is_compiled());
expected_nof_properties += shared->expected_nof_properties(); // <--- overflow here!
} else if (!shared->is_compiled()) {
// In case there was a compilation error for the constructor we will
// throw an error during instantiation. Hence we directly return 0;
result = false;
break;
}
if (!IsDerivedConstructor(shared->kind())) {
break;
}
}
CalculateInstanceSizeHelper(instance_type, true, requested_embedder_fields,
expected_nof_properties, instance_size,
in_object_properties);
return result;
}
By supplying a long prototype chain of objects with a large expected_nof_properties we can control the resulting value of instance_size by causing (requested_embedder_fields + requested_in_object_properties) << kPointerSizeLog2 to be overflown to a small negative value, resulting in an allocation smaller than header_size, which is the minimum required size for the base object class being allocated. This results in memory corruption when the object is initialised/used.
void JSFunction::CalculateInstanceSizeHelper(InstanceType instance_type,
bool has_prototype_slot,
int requested_embedder_fields,
int requested_in_object_properties,
int* instance_size,
int* in_object_properties) {
int header_size = JSObject::GetHeaderSize(instance_type, has_prototype_slot);
DCHECK_LE(requested_embedder_fields,
(JSObject::kMaxInstanceSize - header_size) >> kPointerSizeLog2);
*instance_size =
Min(header_size +
((requested_embedder_fields + requested_in_object_properties)
<< kPointerSizeLog2),
JSObject::kMaxInstanceSize);
*in_object_properties = ((*instance_size - header_size) >> kPointerSizeLog2) -
requested_embedder_fields;
}
The attached PoC crashes current stable on linux.
See crash report ID: 307546648ba8a84a
Chrome issue is https://bugs.chromium.org/p/chromium/issues/detail?id=808192
Attaching the working exploit for this issue.
Note that issue_808192.html is a template - it requires server.py to do a version check and patch a few version dependent constants in, since some object layouts have changed during the range of Chrome versions on which the exploit was tested.
Proof of Concept:
https://gitlab.com/exploit-database/exploitdb-bin-sploits/-/raw/main/bin-sploits/44584.zip