Fix potential use-after-free of V8TrackArrayBuffer (fixes issue #3074)

This commit is contained in:
Marshall Greenblatt 2021-03-23 15:58:17 -04:00
parent 96404f1fd9
commit 91ecc85e93
2 changed files with 18 additions and 45 deletions

View File

@ -318,18 +318,14 @@ class V8TrackArrayBuffer : public CefTrackNode {
public: public:
explicit V8TrackArrayBuffer( explicit V8TrackArrayBuffer(
v8::Isolate* isolate, v8::Isolate* isolate,
void* buffer,
CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback) CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback)
: isolate_(isolate), : isolate_(isolate), release_callback_(release_callback) {
buffer_(buffer),
release_callback_(release_callback) {
DCHECK(isolate_); DCHECK(isolate_);
isolate_->AdjustAmountOfExternalAllocatedMemory( isolate_->AdjustAmountOfExternalAllocatedMemory(
static_cast<int>(sizeof(V8TrackArrayBuffer))); static_cast<int>(sizeof(V8TrackArrayBuffer)));
} }
~V8TrackArrayBuffer() { ~V8TrackArrayBuffer() {
ReleaseBuffer();
isolate_->AdjustAmountOfExternalAllocatedMemory( isolate_->AdjustAmountOfExternalAllocatedMemory(
-static_cast<int>(sizeof(V8TrackArrayBuffer))); -static_cast<int>(sizeof(V8TrackArrayBuffer)));
} }
@ -338,15 +334,6 @@ class V8TrackArrayBuffer : public CefTrackNode {
return release_callback_; return release_callback_;
} }
void ReleaseBuffer() {
if (buffer_ && release_callback_) {
release_callback_->ReleaseBuffer(buffer_);
}
Detach();
}
void Detach() { buffer_ = nullptr; }
// Attach this track object to the specified V8 object. // Attach this track object to the specified V8 object.
void AttachTo(v8::Local<v8::Context> context, void AttachTo(v8::Local<v8::Context> context,
v8::Local<v8::ArrayBuffer> arrayBuffer) { v8::Local<v8::ArrayBuffer> arrayBuffer) {
@ -367,7 +354,6 @@ class V8TrackArrayBuffer : public CefTrackNode {
private: private:
v8::Isolate* isolate_; v8::Isolate* isolate_;
void* buffer_;
CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback_; CefRefPtr<CefV8ArrayBufferReleaseCallback> release_callback_;
}; };
@ -1414,17 +1400,22 @@ CefRefPtr<CefV8Value> CefV8Value::CreateArrayBuffer(
// Create a tracker object that will cause the user data reference to be // Create a tracker object that will cause the user data reference to be
// released when the V8 object is destroyed. // released when the V8 object is destroyed.
V8TrackArrayBuffer* tracker = V8TrackArrayBuffer* tracker =
new V8TrackArrayBuffer(isolate, buffer, release_callback); new V8TrackArrayBuffer(isolate, release_callback);
if (release_callback)
release_callback->AddRef();
auto deleter = [](void* data, size_t length, void* deleter_data) { auto deleter = [](void* data, size_t length, void* deleter_data) {
auto* tracker = reinterpret_cast<V8TrackArrayBuffer*>(deleter_data); auto* release_callback =
if (tracker) { reinterpret_cast<CefV8ArrayBufferReleaseCallback*>(deleter_data);
tracker->ReleaseBuffer(); if (release_callback) {
release_callback->ReleaseBuffer(data);
release_callback->Release();
} }
}; };
std::unique_ptr<v8::BackingStore> backing = std::unique_ptr<v8::BackingStore> backing = v8::ArrayBuffer::NewBackingStore(
v8::ArrayBuffer::NewBackingStore(buffer, length, deleter, tracker); buffer, length, deleter, release_callback.get());
v8::Local<v8::ArrayBuffer> ab = v8::Local<v8::ArrayBuffer> ab =
v8::ArrayBuffer::New(isolate, std::move(backing)); v8::ArrayBuffer::New(isolate, std::move(backing));
@ -2302,8 +2293,6 @@ bool CefV8ValueImpl::NeuterArrayBuffer() {
return false; return false;
} }
arr->Detach(); arr->Detach();
V8TrackArrayBuffer* tracker = V8TrackArrayBuffer::Unwrap(context, obj);
tracker->Detach();
return true; return true;
} }

View File

@ -547,48 +547,32 @@ class V8RendererTest : public ClientAppRenderer::Delegate,
bool destructorCalled = false; bool destructorCalled = false;
bool releaseBufferCalled = false; bool releaseBufferCalled = false;
bool neuteredDestructorCalled = false;
bool neuteredReleaseBufferCalled = false;
// Enter the V8 context. // Enter the V8 context.
EXPECT_TRUE(context->Enter()); EXPECT_TRUE(context->Enter());
{ {
int static_data[16]; int static_data[16];
CefRefPtr<CefV8Value> value;
CefRefPtr<TestArrayBufferReleaseCallback> release_callback = CefRefPtr<TestArrayBufferReleaseCallback> release_callback =
new TestArrayBufferReleaseCallback(&destructorCalled, new TestArrayBufferReleaseCallback(&destructorCalled,
&releaseBufferCalled); &releaseBufferCalled);
CefRefPtr<CefV8Value> value = CefV8Value::CreateArrayBuffer(
CefRefPtr<CefV8Value> neuteredValue; static_data, sizeof(static_data), release_callback);
CefRefPtr<TestArrayBufferReleaseCallback> neuteredReleaseCallback =
new TestArrayBufferReleaseCallback(&neuteredDestructorCalled,
&neuteredReleaseBufferCalled);
value = CefV8Value::CreateArrayBuffer(static_data, sizeof(static_data),
release_callback);
neuteredValue = CefV8Value::CreateArrayBuffer(
static_data, sizeof(static_data), neuteredReleaseCallback);
EXPECT_TRUE(value.get()); EXPECT_TRUE(value.get());
EXPECT_TRUE(value->IsArrayBuffer()); EXPECT_TRUE(value->IsArrayBuffer());
EXPECT_TRUE(value->IsObject()); EXPECT_TRUE(value->IsObject());
EXPECT_FALSE(value->HasValue(0)); EXPECT_FALSE(value->HasValue(0));
EXPECT_FALSE(destructorCalled);
EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr); EXPECT_TRUE(value->GetArrayBufferReleaseCallback().get() != nullptr);
EXPECT_TRUE(((TestArrayBufferReleaseCallback*)value EXPECT_TRUE(((TestArrayBufferReleaseCallback*)value
->GetArrayBufferReleaseCallback() ->GetArrayBufferReleaseCallback()
.get()) == release_callback); .get()) == release_callback);
// |neuteredValue| buffer is explicitly freed by NeuterArrayBuffer(). // |Value| buffer is explicitly freed by NeuterArrayBuffer().
EXPECT_FALSE(neuteredReleaseBufferCalled); EXPECT_FALSE(destructorCalled);
EXPECT_TRUE(neuteredValue->NeuterArrayBuffer());
EXPECT_TRUE(neuteredReleaseBufferCalled);
// |value| buffer is implicitly freed when the value goes out of scope.
EXPECT_FALSE(releaseBufferCalled); EXPECT_FALSE(releaseBufferCalled);
EXPECT_TRUE(value->NeuterArrayBuffer());
EXPECT_TRUE(releaseBufferCalled);
} }
// Exit the V8 context. // Exit the V8 context.
EXPECT_TRUE(destructorCalled); EXPECT_TRUE(destructorCalled);
EXPECT_TRUE(releaseBufferCalled);
EXPECT_TRUE(neuteredDestructorCalled);
EXPECT_TRUE(context->Exit()); EXPECT_TRUE(context->Exit());
DestroyTest(); DestroyTest();
} }