Replace size_t with uint64_t in ringbuffer.

Adds overflow check in RingBufferAllocator + unit-test.
Also, update clients to use uint64_t to avoid casts or narrowing.

BUG=dawn:233

Change-Id: I652e3142407006d082491add600371f95d44741a
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/12380
Reviewed-by: Austin Eng <enga@chromium.org>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Commit-Queue: Bryan Bernhart <bryan.bernhart@intel.com>
diff --git a/src/dawn_native/DynamicUploader.cpp b/src/dawn_native/DynamicUploader.cpp
index 876b789..9996ee1 100644
--- a/src/dawn_native/DynamicUploader.cpp
+++ b/src/dawn_native/DynamicUploader.cpp
@@ -18,7 +18,7 @@
 
 namespace dawn_native {
 
-    DynamicUploader::DynamicUploader(DeviceBase* device, size_t size) : mDevice(device) {
+    DynamicUploader::DynamicUploader(DeviceBase* device, uint64_t size) : mDevice(device) {
         mRingBuffers.emplace_back(
             std::unique_ptr<RingBuffer>(new RingBuffer{nullptr, RingBufferAllocator(size)}));
     }
@@ -28,7 +28,7 @@
                                         mDevice->GetPendingCommandSerial());
     }
 
-    ResultOrError<UploadHandle> DynamicUploader::Allocate(size_t allocationSize, Serial serial) {
+    ResultOrError<UploadHandle> DynamicUploader::Allocate(uint64_t allocationSize, Serial serial) {
         // Note: Validation ensures size is already aligned.
         // First-fit: find next smallest buffer large enough to satisfy the allocation request.
         RingBuffer* targetRingBuffer = mRingBuffers.back().get();
@@ -36,7 +36,7 @@
             const RingBufferAllocator& ringBufferAllocator = ringBuffer->mAllocator;
             // Prevent overflow.
             ASSERT(ringBufferAllocator.GetSize() >= ringBufferAllocator.GetUsedSize());
-            const size_t remainingSize =
+            const uint64_t remainingSize =
                 ringBufferAllocator.GetSize() - ringBufferAllocator.GetUsedSize();
             if (allocationSize <= remainingSize) {
                 targetRingBuffer = ringBuffer.get();
@@ -44,7 +44,7 @@
             }
         }
 
-        size_t startOffset = RingBufferAllocator::kInvalidOffset;
+        uint64_t startOffset = RingBufferAllocator::kInvalidOffset;
         if (targetRingBuffer != nullptr) {
             startOffset = targetRingBuffer->mAllocator.Allocate(allocationSize, serial);
         }
diff --git a/src/dawn_native/DynamicUploader.h b/src/dawn_native/DynamicUploader.h
index f0d4510..068eebd 100644
--- a/src/dawn_native/DynamicUploader.h
+++ b/src/dawn_native/DynamicUploader.h
@@ -25,13 +25,13 @@
 
     struct UploadHandle {
         uint8_t* mappedBuffer = nullptr;
-        size_t startOffset = 0;
+        uint64_t startOffset = 0;
         StagingBufferBase* stagingBuffer = nullptr;
     };
 
     class DynamicUploader {
       public:
-        DynamicUploader(DeviceBase* device, size_t size = kBaseUploadBufferSize);
+        DynamicUploader(DeviceBase* device, uint64_t size = kBaseUploadBufferSize);
         ~DynamicUploader() = default;
 
         // We add functions to Release StagingBuffers to the DynamicUploader as there's
@@ -40,12 +40,12 @@
         // implemented.
         void ReleaseStagingBuffer(std::unique_ptr<StagingBufferBase> stagingBuffer);
 
-        ResultOrError<UploadHandle> Allocate(size_t allocationSize, Serial serial);
+        ResultOrError<UploadHandle> Allocate(uint64_t allocationSize, Serial serial);
         void Deallocate(Serial lastCompletedSerial);
 
       private:
         // TODO(bryan.bernhart@intel.com): Figure out this value.
-        static constexpr size_t kBaseUploadBufferSize = 64000;
+        static constexpr uint64_t kBaseUploadBufferSize = 64000;
 
         struct RingBuffer {
             std::unique_ptr<StagingBufferBase> mStagingBuffer;
diff --git a/src/dawn_native/RingBufferAllocator.cpp b/src/dawn_native/RingBufferAllocator.cpp
index 6cb94b7..6c3eef0 100644
--- a/src/dawn_native/RingBufferAllocator.cpp
+++ b/src/dawn_native/RingBufferAllocator.cpp
@@ -29,7 +29,7 @@
 // TODO(bryan.bernhart@intel.com): Follow-up with ringbuffer optimization.
 namespace dawn_native {
 
-    RingBufferAllocator::RingBufferAllocator(size_t maxSize) : mMaxBlockSize(maxSize) {
+    RingBufferAllocator::RingBufferAllocator(uint64_t maxSize) : mMaxBlockSize(maxSize) {
     }
 
     void RingBufferAllocator::Deallocate(Serial lastCompletedSerial) {
@@ -43,11 +43,11 @@
         mInflightRequests.ClearUpTo(lastCompletedSerial);
     }
 
-    size_t RingBufferAllocator::GetSize() const {
+    uint64_t RingBufferAllocator::GetSize() const {
         return mMaxBlockSize;
     }
 
-    size_t RingBufferAllocator::GetUsedSize() const {
+    uint64_t RingBufferAllocator::GetUsedSize() const {
         return mUsedSize;
     }
 
@@ -62,7 +62,7 @@
     // queue, which identifies an existing (or new) frames-worth of resources. Internally, the
     // ring-buffer maintains offsets of 3 "memory" states: Free, Reclaimed, and Used. This is done
     // in FIFO order as older frames would free resources before newer ones.
-    size_t RingBufferAllocator::Allocate(size_t allocationSize, Serial serial) {
+    uint64_t RingBufferAllocator::Allocate(uint64_t allocationSize, Serial serial) {
         // Check if the buffer is full by comparing the used size.
         // If the buffer is not split where waste occurs (e.g. cannot fit new sub-alloc in front), a
         // subsequent sub-alloc could fail where the used size was previously adjusted to include
@@ -71,7 +71,13 @@
             return kInvalidOffset;
         }
 
-        size_t startOffset = kInvalidOffset;
+        // Ensure adding allocationSize does not overflow.
+        const uint64_t remainingSize = (mMaxBlockSize - mUsedSize);
+        if (allocationSize > remainingSize) {
+            return kInvalidOffset;
+        }
+
+        uint64_t startOffset = kInvalidOffset;
 
         // Check if the buffer is NOT split (i.e sub-alloc on ends)
         if (mUsedStartOffset <= mUsedEndOffset) {
@@ -86,7 +92,7 @@
             } else if (allocationSize <= mUsedStartOffset) {  // Try to sub-alloc at front.
                 // Count the space at the end so that a subsequent
                 // sub-alloc cannot not succeed when the buffer is full.
-                const size_t requestSize = (mMaxBlockSize - mUsedEndOffset) + allocationSize;
+                const uint64_t requestSize = (mMaxBlockSize - mUsedEndOffset) + allocationSize;
 
                 startOffset = 0;
                 mUsedEndOffset = allocationSize;
diff --git a/src/dawn_native/RingBufferAllocator.h b/src/dawn_native/RingBufferAllocator.h
index 60ee639..e437632 100644
--- a/src/dawn_native/RingBufferAllocator.h
+++ b/src/dawn_native/RingBufferAllocator.h
@@ -26,32 +26,32 @@
     class RingBufferAllocator {
       public:
         RingBufferAllocator() = default;
-        RingBufferAllocator(size_t maxSize);
+        RingBufferAllocator(uint64_t maxSize);
         ~RingBufferAllocator() = default;
 
-        size_t Allocate(size_t allocationSize, Serial serial);
+        uint64_t Allocate(uint64_t allocationSize, Serial serial);
         void Deallocate(Serial lastCompletedSerial);
 
-        size_t GetSize() const;
+        uint64_t GetSize() const;
         bool Empty() const;
-        size_t GetUsedSize() const;
+        uint64_t GetUsedSize() const;
 
-        static constexpr size_t kInvalidOffset = std::numeric_limits<size_t>::max();
+        static constexpr uint64_t kInvalidOffset = std::numeric_limits<uint64_t>::max();
 
       private:
         struct Request {
-            size_t endOffset;
-            size_t size;
+            uint64_t endOffset;
+            uint64_t size;
         };
 
         SerialQueue<Request> mInflightRequests;  // Queue of the recorded sub-alloc requests (e.g.
                                                  // frame of resources).
 
-        size_t mUsedEndOffset = 0;    // Tail of used sub-alloc requests (in bytes).
-        size_t mUsedStartOffset = 0;  // Head of used sub-alloc requests (in bytes).
-        size_t mMaxBlockSize = 0;     // Max size of the ring buffer (in bytes).
-        size_t mUsedSize = 0;  // Size of the sub-alloc requests (in bytes) of the ring buffer.
-        size_t mCurrentRequestSize =
+        uint64_t mUsedEndOffset = 0;    // Tail of used sub-alloc requests (in bytes).
+        uint64_t mUsedStartOffset = 0;  // Head of used sub-alloc requests (in bytes).
+        uint64_t mMaxBlockSize = 0;     // Max size of the ring buffer (in bytes).
+        uint64_t mUsedSize = 0;  // Size of the sub-alloc requests (in bytes) of the ring buffer.
+        uint64_t mCurrentRequestSize =
             0;  // Size of the sub-alloc requests (in bytes) of the current serial.
     };
 }  // namespace dawn_native
diff --git a/src/dawn_native/d3d12/DescriptorHeapAllocator.cpp b/src/dawn_native/d3d12/DescriptorHeapAllocator.cpp
index a69641a..1980452 100644
--- a/src/dawn_native/d3d12/DescriptorHeapAllocator.cpp
+++ b/src/dawn_native/d3d12/DescriptorHeapAllocator.cpp
@@ -25,7 +25,7 @@
 
     DescriptorHeapHandle::DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap,
                                                uint32_t sizeIncrement,
-                                               uint32_t offset)
+                                               uint64_t offset)
         : mDescriptorHeap(descriptorHeap), mSizeIncrement(sizeIncrement), mOffset(offset) {
     }
 
@@ -68,12 +68,11 @@
         DescriptorHeapInfo* heapInfo,
         D3D12_DESCRIPTOR_HEAP_FLAGS flags) {
         const Serial pendingSerial = mDevice->GetPendingCommandSerial();
-        size_t startOffset = (heapInfo->heap == nullptr)
-                                 ? RingBufferAllocator::kInvalidOffset
-                                 : heapInfo->allocator.Allocate(count, pendingSerial);
+        uint64_t startOffset = (heapInfo->heap == nullptr)
+                                   ? RingBufferAllocator::kInvalidOffset
+                                   : heapInfo->allocator.Allocate(count, pendingSerial);
         if (startOffset != RingBufferAllocator::kInvalidOffset) {
-            return DescriptorHeapHandle{heapInfo->heap, mSizeIncrements[type],
-                                        static_cast<uint32_t>(startOffset)};
+            return DescriptorHeapHandle{heapInfo->heap, mSizeIncrements[type], startOffset};
         }
 
         // If the pool has no more space, replace the pool with a new one of the specified size
diff --git a/src/dawn_native/d3d12/DescriptorHeapAllocator.h b/src/dawn_native/d3d12/DescriptorHeapAllocator.h
index e4949a6..bcb6ff5 100644
--- a/src/dawn_native/d3d12/DescriptorHeapAllocator.h
+++ b/src/dawn_native/d3d12/DescriptorHeapAllocator.h
@@ -33,7 +33,7 @@
         DescriptorHeapHandle();
         DescriptorHeapHandle(ComPtr<ID3D12DescriptorHeap> descriptorHeap,
                              uint32_t sizeIncrement,
-                             uint32_t offset);
+                             uint64_t offset);
 
         ID3D12DescriptorHeap* Get() const;
         D3D12_CPU_DESCRIPTOR_HANDLE GetCPUHandle(uint32_t index) const;
@@ -42,7 +42,7 @@
       private:
         ComPtr<ID3D12DescriptorHeap> mDescriptorHeap;
         uint32_t mSizeIncrement;
-        uint32_t mOffset;
+        uint64_t mOffset;
     };
 
     class DescriptorHeapAllocator {
diff --git a/src/tests/unittests/RingBufferAllocatorTests.cpp b/src/tests/unittests/RingBufferAllocatorTests.cpp
index 2455e8d..a3e5a62 100644
--- a/src/tests/unittests/RingBufferAllocatorTests.cpp
+++ b/src/tests/unittests/RingBufferAllocatorTests.cpp
@@ -18,11 +18,11 @@
 
 using namespace dawn_native;
 
-constexpr size_t RingBufferAllocator::kInvalidOffset;
+constexpr uint64_t RingBufferAllocator::kInvalidOffset;
 
 // Number of basic tests for Ringbuffer
 TEST(RingBufferAllocatorTests, BasicTest) {
-    constexpr size_t sizeInBytes = 64000;
+    constexpr uint64_t sizeInBytes = 64000;
     RingBufferAllocator allocator(sizeInBytes);
 
     // Ensure no requests exist on empty buffer.
@@ -43,8 +43,8 @@
 
 // Tests that several ringbuffer allocations do not fail.
 TEST(RingBufferAllocatorTests, RingBufferManyAlloc) {
-    constexpr size_t maxNumOfFrames = 64000;
-    constexpr size_t frameSizeInBytes = 4;
+    constexpr uint64_t maxNumOfFrames = 64000;
+    constexpr uint64_t frameSizeInBytes = 4;
 
     RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
 
@@ -57,8 +57,8 @@
 
 // Tests ringbuffer sub-allocations of the same serial are correctly tracked.
 TEST(RingBufferAllocatorTests, AllocInSameFrame) {
-    constexpr size_t maxNumOfFrames = 3;
-    constexpr size_t frameSizeInBytes = 4;
+    constexpr uint64_t maxNumOfFrames = 3;
+    constexpr uint64_t frameSizeInBytes = 4;
 
     RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
 
@@ -87,8 +87,8 @@
 
 // Tests ringbuffer sub-allocation at various offsets.
 TEST(RingBufferAllocatorTests, RingBufferSubAlloc) {
-    constexpr size_t maxNumOfFrames = 10;
-    constexpr size_t frameSizeInBytes = 4;
+    constexpr uint64_t maxNumOfFrames = 10;
+    constexpr uint64_t frameSizeInBytes = 4;
 
     RingBufferAllocator allocator(maxNumOfFrames * frameSizeInBytes);
 
@@ -164,3 +164,14 @@
 
     EXPECT_TRUE(allocator.Empty());
 }
+
+// Checks if ringbuffer sub-allocation does not overflow.
+TEST(RingBufferAllocatorTests, RingBufferOverflow) {
+    Serial serial = 1;
+
+    RingBufferAllocator allocator(std::numeric_limits<uint64_t>::max());
+
+    ASSERT_EQ(allocator.Allocate(1, serial), 0u);
+    ASSERT_EQ(allocator.Allocate(std::numeric_limits<uint64_t>::max(), serial + 1),
+              RingBufferAllocator::kInvalidOffset);
+}
\ No newline at end of file