Skip to content

Commit 8c03dd0

Browse files
verwaestmibrunin
authored andcommitted
[Backport] CVE-2024-0519: Out of bounds memory access in V8
Manual cherry-pick of patch originally reviewed on https://chromium-review.googlesource.com/c/v8/v8/+/5192447: Merged: [runtime] Drop fast last-property deletion This interacts badly with other optimizations and isn't particularly common. Bug: chromium:1517354 (cherry picked from commit 389ea9be7d68bb189e16da79f6414edbd4f7594f) Change-Id: Ie16aa38e8984c4879491c0d9a0ca9df0e041fd1d Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/5192447 Auto-Submit: Toon Verwaest <[email protected]> Reviewed-by: Leszek Swirski <[email protected]> Cr-Commit-Position: refs/branch-heads/12.0@{#32} Cr-Branched-From: ed7b4caf1fb8184ad9e24346c84424055d4d430a-refs/heads/12.0.267@{#1} Cr-Branched-From: 210e75b19db4352c9b78dce0bae11c2dc3077df4-refs/heads/main@{#90651} Reviewed-on: https://codereview.qt-project.org/c/qt/qtwebengine-chromium/+/531980 Reviewed-by: Allan Sandfeld Jensen <[email protected]>
1 parent 1620885 commit 8c03dd0

File tree

1 file changed

+0
-174
lines changed

1 file changed

+0
-174
lines changed

chromium/v8/src/runtime/runtime-object.cc

Lines changed: 0 additions & 174 deletions
Original file line numberDiff line numberDiff line change
@@ -71,184 +71,10 @@ MaybeHandle<Object> Runtime::HasProperty(Isolate* isolate,
7171
return ReadOnlyRoots(isolate).boolean_value_handle(maybe.FromJust());
7272
}
7373

74-
namespace {
75-
76-
// This function sets the sentinel value in a deleted field. Thes sentinel has
77-
// to look like a proper standalone object because the slack tracking may
78-
// complete at any time. For this reason we use the filler map word.
79-
// If V8_MAP_PACKING is enabled, then the filler map word is a packed filler
80-
// map. Otherwise, the filler map word is the same as the filler map.
81-
inline void ClearField(Isolate* isolate, JSObject object, FieldIndex index) {
82-
if (index.is_inobject()) {
83-
MapWord filler_map_word =
84-
ReadOnlyRoots(isolate).one_pointer_filler_map_word();
85-
#ifndef V8_MAP_PACKING
86-
DCHECK_EQ(filler_map_word.ToMap(),
87-
ReadOnlyRoots(isolate).one_pointer_filler_map());
88-
#endif
89-
int offset = index.offset();
90-
TaggedField<MapWord>::Release_Store(object, offset, filler_map_word);
91-
} else {
92-
object->property_array()->set(
93-
index.outobject_array_index(),
94-
ReadOnlyRoots(isolate).one_pointer_filler_map());
95-
}
96-
}
97-
98-
void GeneralizeAllTransitionsToFieldAsMutable(Isolate* isolate, Handle<Map> map,
99-
Handle<Name> name) {
100-
InternalIndex descriptor(map->NumberOfOwnDescriptors());
101-
102-
Handle<Map> target_maps[kPropertyAttributesCombinationsCount];
103-
int target_maps_count = 0;
104-
105-
// Collect all outgoing field transitions.
106-
{
107-
DisallowGarbageCollection no_gc;
108-
TransitionsAccessor transitions(isolate, *map);
109-
transitions.ForEachTransitionTo(
110-
*name,
111-
[&](Map target) {
112-
DCHECK_EQ(descriptor, target->LastAdded());
113-
DCHECK_EQ(*name, target->GetLastDescriptorName(isolate));
114-
PropertyDetails details = target->GetLastDescriptorDetails(isolate);
115-
// Currently, we track constness only for fields.
116-
if (details.kind() == PropertyKind::kData &&
117-
details.constness() == PropertyConstness::kConst) {
118-
target_maps[target_maps_count++] = handle(target, isolate);
119-
}
120-
DCHECK_IMPLIES(details.kind() == PropertyKind::kAccessor,
121-
details.constness() == PropertyConstness::kConst);
122-
},
123-
&no_gc);
124-
CHECK_LE(target_maps_count, kPropertyAttributesCombinationsCount);
125-
}
126-
127-
for (int i = 0; i < target_maps_count; i++) {
128-
Handle<Map> target = target_maps[i];
129-
PropertyDetails details =
130-
target->instance_descriptors(isolate)->GetDetails(descriptor);
131-
Handle<FieldType> field_type(
132-
target->instance_descriptors(isolate)->GetFieldType(descriptor),
133-
isolate);
134-
MapUpdater::GeneralizeField(isolate, target, descriptor,
135-
PropertyConstness::kMutable,
136-
details.representation(), field_type);
137-
DCHECK_EQ(PropertyConstness::kMutable, target->instance_descriptors(isolate)
138-
->GetDetails(descriptor)
139-
.constness());
140-
}
141-
}
142-
143-
bool DeleteObjectPropertyFast(Isolate* isolate, Handle<JSReceiver> receiver,
144-
Handle<Object> raw_key) {
145-
// This implements a special case for fast property deletion: when the
146-
// last property in an object is deleted, then instead of normalizing
147-
// the properties, we can undo the last map transition, with a few
148-
// prerequisites:
149-
// (1) The receiver must be a regular object and the key a unique name.
150-
Handle<Map> receiver_map(receiver->map(), isolate);
151-
if (IsSpecialReceiverMap(*receiver_map)) return false;
152-
DCHECK(IsJSObjectMap(*receiver_map));
153-
154-
if (!IsUniqueName(*raw_key)) return false;
155-
Handle<Name> key = Handle<Name>::cast(raw_key);
156-
// (2) The property to be deleted must be the last property.
157-
int nof = receiver_map->NumberOfOwnDescriptors();
158-
if (nof == 0) return false;
159-
InternalIndex descriptor(nof - 1);
160-
Handle<DescriptorArray> descriptors(
161-
receiver_map->instance_descriptors(isolate), isolate);
162-
if (descriptors->GetKey(descriptor) != *key) return false;
163-
// (3) The property to be deleted must be deletable.
164-
PropertyDetails details = descriptors->GetDetails(descriptor);
165-
if (!details.IsConfigurable()) return false;
166-
// (4) The map must have a back pointer.
167-
Handle<Object> backpointer(receiver_map->GetBackPointer(), isolate);
168-
if (!IsMap(*backpointer)) return false;
169-
Handle<Map> parent_map = Handle<Map>::cast(backpointer);
170-
// (5) The last transition must have been caused by adding a property
171-
// (and not any kind of special transition).
172-
if (parent_map->NumberOfOwnDescriptors() != nof - 1) return false;
173-
174-
// Preconditions successful. No more bailouts after this point.
175-
176-
// Zap the property to avoid keeping objects alive. Zapping is not necessary
177-
// for properties stored in the descriptor array.
178-
if (details.location() == PropertyLocation::kField) {
179-
DisallowGarbageCollection no_gc;
180-
181-
// Invalidate slots manually later in case we delete an in-object tagged
182-
// property. In this case we might later store an untagged value in the
183-
// recorded slot.
184-
isolate->heap()->NotifyObjectLayoutChange(*receiver, no_gc,
185-
InvalidateRecordedSlots::kNo);
186-
FieldIndex index =
187-
FieldIndex::ForPropertyIndex(*receiver_map, details.field_index());
188-
// Special case deleting the last out-of object property.
189-
if (!index.is_inobject() && index.outobject_array_index() == 0) {
190-
DCHECK(!parent_map->HasOutOfObjectProperties());
191-
// Clear out the properties backing store.
192-
receiver->SetProperties(ReadOnlyRoots(isolate).empty_fixed_array());
193-
} else {
194-
ClearField(isolate, JSObject::cast(*receiver), index);
195-
if (index.is_inobject()) {
196-
// We need to clear the recorded slot in this case because in-object
197-
// slack tracking might not be finished. This ensures that we don't
198-
// have recorded slots in free space.
199-
isolate->heap()->ClearRecordedSlot(*receiver,
200-
receiver->RawField(index.offset()));
201-
}
202-
}
203-
}
204-
// If the {receiver_map} was marked stable before, then there could be
205-
// optimized code that depends on the assumption that no object that
206-
// reached this {receiver_map} transitions away from it without triggering
207-
// the "deoptimize dependent code" mechanism.
208-
receiver_map->NotifyLeafMapLayoutChange(isolate);
209-
// Finally, perform the map rollback.
210-
receiver->set_map(*parent_map, kReleaseStore);
211-
#if VERIFY_HEAP
212-
if (v8_flags.verify_heap) {
213-
receiver->HeapObjectVerify(isolate);
214-
receiver->property_array()->PropertyArrayVerify(isolate);
215-
}
216-
#endif
217-
218-
// If the {descriptor} was "const" so far, we need to update the
219-
// {receiver_map} here, otherwise we could get the constants wrong, i.e.
220-
//
221-
// o.x = 1;
222-
// [change o.x's attributes or reconfigure property kind]
223-
// delete o.x;
224-
// o.x = 2;
225-
//
226-
// could trick V8 into thinking that `o.x` is still 1 even after the second
227-
// assignment.
228-
229-
// Step 1: Migrate object to an up-to-date shape.
230-
if (parent_map->is_deprecated()) {
231-
JSObject::MigrateInstance(isolate, Handle<JSObject>::cast(receiver));
232-
parent_map = handle(receiver->map(), isolate);
233-
}
234-
235-
// Step 2: Mark outgoing transitions from the up-to-date version of the
236-
// parent_map to same property name of any kind or attributes as mutable.
237-
// Also migrate object to the up-to-date map to make the object shapes
238-
// converge sooner.
239-
GeneralizeAllTransitionsToFieldAsMutable(isolate, parent_map, key);
240-
241-
return true;
242-
}
243-
244-
} // namespace
245-
24674
Maybe<bool> Runtime::DeleteObjectProperty(Isolate* isolate,
24775
Handle<JSReceiver> receiver,
24876
Handle<Object> key,
24977
LanguageMode language_mode) {
250-
if (DeleteObjectPropertyFast(isolate, receiver, key)) return Just(true);
251-
25278
bool success = false;
25379
PropertyKey lookup_key(isolate, key, &success);
25480
if (!success) return Nothing<bool>();

0 commit comments

Comments
 (0)