Fix DynamicUploader use-after-free on ReduceMemoryUsage
Make DynamicUploader work correctly if its list of ring buffer is empty
e.g. after calling ReduceMemoryUsage(). Also, make the ReduceMemoryUsage
test exercise this case.
Bug: 357139493
Change-Id: I87140ca65ad45a276128ba88ba9b738c2c32680b
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/202874
Commit-Queue: Austin Eng <enga@chromium.org>
Auto-Submit: Sunny Sachanandani <sunnyps@chromium.org>
Commit-Queue: Sunny Sachanandani <sunnyps@chromium.org>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/src/dawn/native/DynamicUploader.cpp b/src/dawn/native/DynamicUploader.cpp
index 77930ac..7c3693a 100644
--- a/src/dawn/native/DynamicUploader.cpp
+++ b/src/dawn/native/DynamicUploader.cpp
@@ -36,10 +36,7 @@
namespace dawn::native {
-DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) {
- mRingBuffers.emplace_back(
- std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)}));
-}
+DynamicUploader::DynamicUploader(DeviceBase* device) : mDevice(device) {}
void DynamicUploader::ReleaseStagingBuffer(Ref<BufferBase> stagingBuffer) {
mReleasedStagingBuffers.Enqueue(std::move(stagingBuffer),
@@ -69,6 +66,11 @@
return uploadHandle;
}
+ if (mRingBuffers.empty()) {
+ mRingBuffers.emplace_back(std::unique_ptr<RingBuffer>(
+ new RingBuffer{nullptr, RingBufferAllocator(kRingBufferSize)}));
+ }
+
// Note: Validation ensures size is already aligned.
// First-fit: find next buffer large enough to satisfy the allocation request.
uint64_t startOffset = RingBufferAllocator::kInvalidOffset;
diff --git a/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp b/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
index ca1dfee..cbec419 100644
--- a/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
+++ b/src/dawn/tests/unittests/native/MemoryInstrumentationTests.cpp
@@ -224,19 +224,26 @@
uniformBuffer.Destroy();
- wgpu::Future completionFuture = device.GetQueue().OnSubmittedWorkDone(
- wgpu::CallbackMode::WaitAnyOnly, [](wgpu::QueueWorkDoneStatus status) {});
-
- wgpu::WaitStatus waitStatus = wgpu::WaitStatus::TimedOut;
- while (waitStatus != wgpu::WaitStatus::Success) {
- std::this_thread::sleep_for(std::chrono::milliseconds(100));
- waitStatus = wgpu::Instance(ToAPI(mDeviceMock->GetInstance())).WaitAny(completionFuture, 0);
- }
+ mDeviceMock->GetInstance()->APIProcessEvents();
// DynamicUploader buffers will still be alive.
EXPECT_GT(ComputeEstimatedMemoryUsage(device.Get()), uint64_t(0));
ReduceMemoryUsage(device.Get());
+ // But not any more.
EXPECT_EQ(ComputeEstimatedMemoryUsage(device.Get()), uint64_t(0));
+
+ // Check that DynamicUploader buffer is recreated again.
+ uniformBuffer = device.CreateBuffer(&kBufferDesc);
+ EXPECT_TRUE(uniformBuffer);
+
+ device.GetQueue().WriteBuffer(uniformBuffer, 0, zeroes.data(), zeroes.size());
+ device.GetQueue().Submit(0, nullptr);
+
+ uniformBuffer.Destroy();
+
+ mDeviceMock->GetInstance()->APIProcessEvents();
+
+ EXPECT_GT(ComputeEstimatedMemoryUsage(device.Get()), uint64_t(0));
}
} // namespace