Skip to content

Commit 524e681

Browse files
pthierV8 LUCI CQ
authored andcommitted
[sandbox] Fix ExternalString --> ThinString transition
When an ExternalString is transitioned to a ThinString, we need to notify the GC about the layout change. In addition we need to use slot snapshotting for external strings in concurrent marking to avoid interpreting stale slots as external pointers. Bug: chromium:1370303 Change-Id: Ibcf6c1eafb31df392d97a4761e006b9d3507bd5f Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/3936151 Reviewed-by: Dominik Inführ <[email protected]> Commit-Queue: Patrick Thier <[email protected]> Cr-Commit-Position: refs/heads/main@{#83552}
1 parent 06e6427 commit 524e681

2 files changed

Lines changed: 79 additions & 13 deletions

File tree

src/heap/concurrent-marking.cc

Lines changed: 66 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -68,21 +68,48 @@ class ConcurrentMarkingState final
6868
// Helper class for storing in-object slot addresses and values.
6969
class SlotSnapshot {
7070
public:
71-
SlotSnapshot() : number_of_slots_(0) {}
71+
SlotSnapshot()
72+
: number_of_object_slots_(0), number_of_external_pointer_slots_(0) {}
7273
SlotSnapshot(const SlotSnapshot&) = delete;
7374
SlotSnapshot& operator=(const SlotSnapshot&) = delete;
74-
int number_of_slots() const { return number_of_slots_; }
75-
ObjectSlot slot(int i) const { return snapshot_[i].first; }
76-
Object value(int i) const { return snapshot_[i].second; }
77-
void clear() { number_of_slots_ = 0; }
75+
int number_of_object_slots() const { return number_of_object_slots_; }
76+
int number_of_external_pointer_slots() const {
77+
return number_of_external_pointer_slots_;
78+
}
79+
ObjectSlot object_slot(int i) const { return object_snapshot_[i].first; }
80+
Object object_value(int i) const { return object_snapshot_[i].second; }
81+
ExternalPointerSlot external_pointer_slot(int i) const {
82+
return external_pointer_snapshot_[i].first;
83+
}
84+
ExternalPointerTag external_pointer_tag(int i) const {
85+
return external_pointer_snapshot_[i].second;
86+
}
87+
void clear() {
88+
number_of_object_slots_ = 0;
89+
number_of_external_pointer_slots_ = 0;
90+
}
7891
void add(ObjectSlot slot, Object value) {
79-
snapshot_[number_of_slots_++] = {slot, value};
92+
DCHECK_LT(number_of_object_slots_, kMaxObjectSlots);
93+
object_snapshot_[number_of_object_slots_++] = {slot, value};
94+
}
95+
void add(ExternalPointerSlot slot, ExternalPointerTag tag) {
96+
DCHECK_LT(number_of_external_pointer_slots_, kMaxExternalPointerSlots);
97+
external_pointer_snapshot_[number_of_external_pointer_slots_++] = {slot,
98+
tag};
8099
}
81100

82101
private:
83-
static const int kMaxSnapshotSize = JSObject::kMaxInstanceSize / kTaggedSize;
84-
int number_of_slots_;
85-
std::pair<ObjectSlot, Object> snapshot_[kMaxSnapshotSize];
102+
// Maximum number of pointer slots of objects we use snapshotting for.
103+
// ConsStrings can have 3 (Map + Left + Right) pointers.
104+
static constexpr int kMaxObjectSlots = 3;
105+
// Maximum number of external pointer slots of objects we use snapshotting
106+
// for. ExternalStrings can have 2 (resource + cached data) external pointers.
107+
static constexpr int kMaxExternalPointerSlots = 2;
108+
int number_of_object_slots_;
109+
int number_of_external_pointer_slots_;
110+
std::pair<ObjectSlot, Object> object_snapshot_[kMaxObjectSlots];
111+
std::pair<ExternalPointerSlot, ExternalPointerTag>
112+
external_pointer_snapshot_[kMaxExternalPointerSlots];
86113
};
87114

88115
class ConcurrentMarkingVisitorUtility {
@@ -114,9 +141,9 @@ class ConcurrentMarkingVisitorUtility {
114141
template <typename Visitor>
115142
static void VisitPointersInSnapshot(Visitor* visitor, HeapObject host,
116143
const SlotSnapshot& snapshot) {
117-
for (int i = 0; i < snapshot.number_of_slots(); i++) {
118-
ObjectSlot slot = snapshot.slot(i);
119-
Object object = snapshot.value(i);
144+
for (int i = 0; i < snapshot.number_of_object_slots(); i++) {
145+
ObjectSlot slot = snapshot.object_slot(i);
146+
Object object = snapshot.object_value(i);
120147
DCHECK(!HasWeakHeapObjectTag(object));
121148
if (!object.IsHeapObject()) continue;
122149
HeapObject heap_object = HeapObject::cast(object);
@@ -127,6 +154,16 @@ class ConcurrentMarkingVisitorUtility {
127154
}
128155
}
129156

157+
template <typename Visitor>
158+
static void VisitExternalPointersInSnapshot(Visitor* visitor, HeapObject host,
159+
const SlotSnapshot& snapshot) {
160+
for (int i = 0; i < snapshot.number_of_external_pointer_slots(); i++) {
161+
ExternalPointerSlot slot = snapshot.external_pointer_slot(i);
162+
ExternalPointerTag tag = snapshot.external_pointer_tag(i);
163+
visitor->VisitExternalPointer(host, slot, tag);
164+
}
165+
}
166+
130167
template <typename Visitor, typename T>
131168
static int VisitFullyWithSnapshot(Visitor* visitor, Map map, T object) {
132169
using TBodyDescriptor = typename T::BodyDescriptor;
@@ -137,6 +174,8 @@ class ConcurrentMarkingVisitorUtility {
137174
if (!visitor->ShouldVisit(object)) return 0;
138175
ConcurrentMarkingVisitorUtility::VisitPointersInSnapshot(visitor, object,
139176
snapshot);
177+
ConcurrentMarkingVisitorUtility::VisitExternalPointersInSnapshot(
178+
visitor, object, snapshot);
140179
return size;
141180
}
142181

@@ -183,6 +222,11 @@ class ConcurrentMarkingVisitorUtility {
183222
UNREACHABLE();
184223
}
185224

225+
void VisitExternalPointer(HeapObject host, ExternalPointerSlot slot,
226+
ExternalPointerTag tag) override {
227+
slot_snapshot_->add(slot, tag);
228+
}
229+
186230
void VisitCodeTarget(Code host, RelocInfo* rinfo) final {
187231
// This should never happen, because snapshotting is performed only on
188232
// some String subclasses.
@@ -453,6 +497,16 @@ class ConcurrentMarkingVisitor final
453497
return SeqTwoByteString::SizeFor(object.length(kAcquireLoad));
454498
}
455499

500+
int VisitExternalOneByteString(Map map, ExternalOneByteString object) {
501+
return ConcurrentMarkingVisitorUtility::VisitFullyWithSnapshot(this, map,
502+
object);
503+
}
504+
505+
int VisitExternalTwoByteString(Map map, ExternalTwoByteString object) {
506+
return ConcurrentMarkingVisitorUtility::VisitFullyWithSnapshot(this, map,
507+
object);
508+
}
509+
456510
// Implements ephemeron semantics: Marks value if key is already reachable.
457511
// Returns true if value was actually marked.
458512
bool ProcessEphemeron(HeapObject key, HeapObject value) {

src/objects/string.cc

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -223,6 +223,14 @@ void String::MakeThin(IsolateT* isolate, String internalized) {
223223
Map target_map = ComputeThinStringMap(isolate, initial_shape,
224224
internalized.IsOneByteRepresentation());
225225
if (initial_shape.IsExternal()) {
226+
// Notify GC about the layout change before the transition to avoid
227+
// concurrent marking from observing any in-between state (e.g.
228+
// ExternalString map where the resource external pointer is overwritten
229+
// with a tagged pointer).
230+
// ExternalString -> ThinString transitions can only happen on the
231+
// main-thread.
232+
isolate->AsIsolate()->heap()->NotifyObjectLayoutChange(
233+
*this, no_gc, InvalidateRecordedSlots::kYes, ThinString::kSize);
226234
MigrateExternalString(isolate->AsIsolate(), *this, internalized);
227235
}
228236

@@ -231,7 +239,11 @@ void String::MakeThin(IsolateT* isolate, String internalized) {
231239
// ThinString.
232240
ThinString thin = ThinString::unchecked_cast(*this);
233241
thin.set_actual(internalized);
234-
set_map_safe_transition(target_map, kReleaseStore);
242+
if (initial_shape.IsExternal()) {
243+
set_map(target_map, kReleaseStore);
244+
} else {
245+
set_map_safe_transition(target_map, kReleaseStore);
246+
}
235247

236248
DCHECK_GE(old_size, ThinString::kSize);
237249
int size_delta = old_size - ThinString::kSize;

0 commit comments

Comments
 (0)