Skip to content

Commit 3353a7d

Browse files
GeorgNeisCommit Bot
authored andcommitted
[deoptimizer] Fix bug in OptimizedFrame::Summarize
OptimizedFrame::Summarize is used by debugger features etc to inspect the frame of an optimized function (and the virtual frames of functions that got inlined). It could end up materializing a JSArray with the same backing store as one that would later get left-trimmed, resulting in a dangling elements pointer. This CL fixes that by creating a fresh copy of the elements store instead. Bug: chromium:1182647 Change-Id: Iaf329464520a927b0ba33166cad2524d3752c450 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2748593 Reviewed-by: Benedikt Meurer <[email protected]> Commit-Queue: Georg Neis <[email protected]> Cr-Commit-Position: refs/heads/master@{#73330}
1 parent 19d8302 commit 3353a7d

3 files changed

Lines changed: 92 additions & 10 deletions

File tree

src/deoptimizer/translated-state.cc

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1282,7 +1282,8 @@ Address TranslatedState::DecompressIfNeeded(intptr_t value) {
12821282
}
12831283
}
12841284

1285-
TranslatedState::TranslatedState(const JavaScriptFrame* frame) {
1285+
TranslatedState::TranslatedState(const JavaScriptFrame* frame)
1286+
: purpose_(kFrameInspection) {
12861287
int deopt_index = Safepoint::kNoDeoptimizationIndex;
12871288
DeoptimizationData data =
12881289
static_cast<const OptimizedFrame*>(frame)->GetDeoptimizationData(
@@ -1672,25 +1673,63 @@ void TranslatedState::EnsureCapturedObjectAllocatedAt(
16721673
}
16731674

16741675
default:
1675-
CHECK(map->IsJSObjectMap());
16761676
EnsureJSObjectAllocated(slot, map);
1677-
TranslatedValue* properties_slot = &(frame->values_[value_index]);
1678-
value_index++;
1677+
int remaining_children_count = slot->GetChildrenCount() - 1;
1678+
1679+
TranslatedValue* properties_slot = frame->ValueAt(value_index);
1680+
value_index++, remaining_children_count--;
16791681
if (properties_slot->kind() == TranslatedValue::kCapturedObject) {
1680-
// If we are materializing the property array, make sure we put
1681-
// the mutable heap numbers at the right places.
1682+
// We are materializing the property array, so make sure we put the
1683+
// mutable heap numbers at the right places.
16821684
EnsurePropertiesAllocatedAndMarked(properties_slot, map);
16831685
EnsureChildrenAllocated(properties_slot->GetChildrenCount(), frame,
16841686
&value_index, worklist);
1687+
} else {
1688+
CHECK_EQ(properties_slot->kind(), TranslatedValue::kTagged);
16851689
}
1686-
// Make sure all the remaining children (after the map and properties) are
1687-
// allocated.
1688-
return EnsureChildrenAllocated(slot->GetChildrenCount() - 2, frame,
1690+
1691+
TranslatedValue* elements_slot = frame->ValueAt(value_index);
1692+
value_index++, remaining_children_count--;
1693+
if (elements_slot->kind() == TranslatedValue::kCapturedObject ||
1694+
!map->IsJSArrayMap()) {
1695+
// Handle this case with the other remaining children below.
1696+
value_index--, remaining_children_count++;
1697+
} else {
1698+
CHECK_EQ(elements_slot->kind(), TranslatedValue::kTagged);
1699+
elements_slot->GetValue();
1700+
if (purpose_ == kFrameInspection) {
1701+
// We are materializing a JSArray for the purpose of frame inspection.
1702+
// If we were to construct it with the above elements value then an
1703+
// actual deopt later on might create another JSArray instance with
1704+
// the same elements store. That would violate the key assumption
1705+
// behind left-trimming.
1706+
elements_slot->ReplaceElementsArrayWithCopy();
1707+
}
1708+
}
1709+
1710+
// Make sure all the remaining children (after the map, properties store,
1711+
// and possibly elements store) are allocated.
1712+
return EnsureChildrenAllocated(remaining_children_count, frame,
16891713
&value_index, worklist);
16901714
}
16911715
UNREACHABLE();
16921716
}
16931717

1718+
void TranslatedValue::ReplaceElementsArrayWithCopy() {
1719+
DCHECK_EQ(kind(), TranslatedValue::kTagged);
1720+
DCHECK_EQ(materialization_state(), TranslatedValue::kFinished);
1721+
auto elements = Handle<FixedArrayBase>::cast(GetValue());
1722+
DCHECK(elements->IsFixedArray() || elements->IsFixedDoubleArray());
1723+
if (elements->IsFixedDoubleArray()) {
1724+
DCHECK(!elements->IsCowArray());
1725+
set_storage(isolate()->factory()->CopyFixedDoubleArray(
1726+
Handle<FixedDoubleArray>::cast(elements)));
1727+
} else if (!elements->IsCowArray()) {
1728+
set_storage(isolate()->factory()->CopyFixedArray(
1729+
Handle<FixedArray>::cast(elements)));
1730+
}
1731+
}
1732+
16941733
void TranslatedState::EnsureChildrenAllocated(int count, TranslatedFrame* frame,
16951734
int* value_index,
16961735
std::stack<int>* worklist) {
@@ -1755,6 +1794,7 @@ Handle<ByteArray> TranslatedState::AllocateStorageFor(TranslatedValue* slot) {
17551794

17561795
void TranslatedState::EnsureJSObjectAllocated(TranslatedValue* slot,
17571796
Handle<Map> map) {
1797+
CHECK(map->IsJSObjectMap());
17581798
CHECK_EQ(map->instance_size(), slot->GetChildrenCount() * kTaggedSize);
17591799

17601800
Handle<ByteArray> object_storage = AllocateStorageFor(slot);

src/deoptimizer/translated-state.h

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ class TranslatedValue {
128128
return storage_;
129129
}
130130

131+
void ReplaceElementsArrayWithCopy();
132+
131133
Kind kind_;
132134
MaterializationState materialization_state_ = kUninitialized;
133135
TranslatedState* container_; // This is only needed for materialization of
@@ -346,7 +348,15 @@ class TranslatedFrame {
346348

347349
class TranslatedState {
348350
public:
349-
TranslatedState() = default;
351+
// There are two constructors, each for a different purpose:
352+
353+
// The default constructor is for the purpose of deoptimizing an optimized
354+
// frame (replacing it with one or several unoptimized frames). It is used by
355+
// the Deoptimizer.
356+
TranslatedState() : purpose_(kDeoptimization) {}
357+
358+
// This constructor is for the purpose of merely inspecting an optimized
359+
// frame. It is used by stack trace generation and various debugging features.
350360
explicit TranslatedState(const JavaScriptFrame* frame);
351361

352362
void Prepare(Address stack_frame_pointer);
@@ -381,6 +391,12 @@ class TranslatedState {
381391
private:
382392
friend TranslatedValue;
383393

394+
// See the description of the constructors for an explanation of the two
395+
// purposes. The only actual difference is that in the kFrameInspection case
396+
// extra work is needed to not violate assumptions made by left-trimming. For
397+
// details, see the code around ReplaceElementsArrayWithCopy.
398+
enum Purpose { kDeoptimization, kFrameInspection };
399+
384400
TranslatedFrame CreateNextTranslatedFrame(TranslationArrayIterator* iterator,
385401
FixedArray literal_array,
386402
Address fp, FILE* trace_file);
@@ -437,6 +453,7 @@ class TranslatedState {
437453
static Float32 GetFloatSlot(Address fp, int slot_index);
438454
static Float64 GetDoubleSlot(Address fp, int slot_index);
439455

456+
Purpose const purpose_;
440457
std::vector<TranslatedFrame> frames_;
441458
Isolate* isolate_ = nullptr;
442459
Address stack_frame_pointer_ = kNullAddress;
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
// Copyright 2021 the V8 project authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
// Flags: --allow-natives-syntax --verify-heap
6+
7+
function foo() {
8+
const arr = Array(1000);
9+
10+
function bar() {
11+
try { ({a: p4nda, b: arr.length}); } catch(e) {}
12+
}
13+
14+
for (var i = 0; i < 25; i++) bar();
15+
16+
/p4nda/.test({}); // Deopt here.
17+
18+
arr.shift();
19+
}
20+
21+
%PrepareFunctionForOptimization(foo);
22+
foo();
23+
foo();
24+
%OptimizeFunctionOnNextCall(foo);
25+
foo();

0 commit comments

Comments
 (0)