dawn::wire validate that the generation must be strictly increasing

Bug: chromium:1379634
Change-Id: Iaa067a30ac5992adfb5aac168104163a62f8bf9c
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/108549
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Jie A Chen <jie.a.chen@intel.com>
Reviewed-by: Corentin Wallez <cwallez@chromium.org>
Kokoro: Kokoro <noreply+kokoro@google.com>
diff --git a/generator/templates/dawn/wire/server/ServerHandlers.cpp b/generator/templates/dawn/wire/server/ServerHandlers.cpp
index 9a9f05a..fcba3d1 100644
--- a/generator/templates/dawn/wire/server/ServerHandlers.cpp
+++ b/generator/templates/dawn/wire/server/ServerHandlers.cpp
@@ -47,7 +47,7 @@
                 {% set Type = member.handle_type.name.CamelCase() %}
                 {% set name = as_varName(member.name) %}
 
-                auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}}.id);
+                auto* {{name}}Data = {{Type}}Objects().Allocate(cmd.{{name}});
                 if ({{name}}Data == nullptr) {
                     return false;
                 }
diff --git a/src/dawn/tests/unittests/wire/WireInjectTextureTests.cpp b/src/dawn/tests/unittests/wire/WireInjectTextureTests.cpp
index 3438d9b..005522f 100644
--- a/src/dawn/tests/unittests/wire/WireInjectTextureTests.cpp
+++ b/src/dawn/tests/unittests/wire/WireInjectTextureTests.cpp
@@ -72,6 +72,47 @@
                                                 reservation.deviceGeneration));
 }
 
+// Test that injecting the same id without a destroy first fails.
+TEST_F(WireInjectTextureTests, ReuseIDAndGeneration) {
+    // Do this loop multiple times since the first time, we can't test `generation - 1` since
+    // generation == 0.
+    ReservedTexture reservation;
+    WGPUTexture apiTexture = nullptr;
+    for (int i = 0; i < 2; ++i) {
+        reservation = GetWireClient()->ReserveTexture(device, &placeholderDesc);
+
+        apiTexture = api.GetNewTexture();
+        EXPECT_CALL(api, TextureReference(apiTexture));
+        ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id,
+                                                   reservation.generation, reservation.deviceId,
+                                                   reservation.deviceGeneration));
+
+        // Release the texture. It should be possible to reuse the ID now, but not the generation
+        wgpuTextureRelease(reservation.texture);
+        EXPECT_CALL(api, TextureRelease(apiTexture));
+        FlushClient();
+
+        // Invalid to inject with the same ID and generation.
+        ASSERT_FALSE(GetWireServer()->InjectTexture(apiTexture, reservation.id,
+                                                    reservation.generation, reservation.deviceId,
+                                                    reservation.deviceGeneration));
+        if (i > 0) {
+            EXPECT_GE(reservation.generation, 1u);
+
+            // Invalid to inject with the same ID and lesser generation.
+            ASSERT_FALSE(GetWireServer()->InjectTexture(
+                apiTexture, reservation.id, reservation.generation - 1, reservation.deviceId,
+                reservation.deviceGeneration));
+        }
+    }
+
+    // Valid to inject with the same ID and greater generation.
+    EXPECT_CALL(api, TextureReference(apiTexture));
+    ASSERT_TRUE(GetWireServer()->InjectTexture(apiTexture, reservation.id,
+                                               reservation.generation + 1, reservation.deviceId,
+                                               reservation.deviceGeneration));
+}
+
 // Test that the server only borrows the texture and does a single reference-release
 TEST_F(WireInjectTextureTests, InjectedTextureLifetime) {
     ReservedTexture reservation = GetWireClient()->ReserveTexture(device, &placeholderDesc);
diff --git a/src/dawn/wire/server/ObjectStorage.h b/src/dawn/wire/server/ObjectStorage.h
index 1bef0ad..4c72c65 100644
--- a/src/dawn/wire/server/ObjectStorage.h
+++ b/src/dawn/wire/server/ObjectStorage.h
@@ -126,8 +126,8 @@
     // Allocates the data for a given ID and returns it.
     // Returns nullptr if the ID is already allocated, or too far ahead, or if ID is 0 (ID 0 is
     // reserved for nullptr). Invalidates all the Data*
-    Data* Allocate(uint32_t id, AllocationState state = AllocationState::Allocated) {
-        if (id == 0 || id > mKnown.size()) {
+    Data* Allocate(ObjectHandle handle, AllocationState state = AllocationState::Allocated) {
+        if (handle.id == 0 || handle.id > mKnown.size()) {
             return nullptr;
         }
 
@@ -135,17 +135,24 @@
         data.state = state;
         data.handle = nullptr;
 
-        if (id >= mKnown.size()) {
+        if (handle.id >= mKnown.size()) {
             mKnown.push_back(std::move(data));
             return &mKnown.back();
         }
 
-        if (mKnown[id].state != AllocationState::Free) {
+        if (mKnown[handle.id].state != AllocationState::Free) {
             return nullptr;
         }
 
-        mKnown[id] = std::move(data);
-        return &mKnown[id];
+        // The generation should be strictly increasing.
+        if (handle.generation <= mKnown[handle.id].generation) {
+            return nullptr;
+        }
+        // update the generation in the slot
+        data.generation = handle.generation;
+
+        mKnown[handle.id] = std::move(data);
+        return &mKnown[handle.id];
     }
 
     // Marks an ID as deallocated
@@ -193,8 +200,8 @@
   public:
     KnownObjects() = default;
 
-    Data* Allocate(uint32_t id, AllocationState state = AllocationState::Allocated) {
-        Data* data = KnownObjectsBase<WGPUDevice>::Allocate(id, state);
+    Data* Allocate(ObjectHandle handle, AllocationState state = AllocationState::Allocated) {
+        Data* data = KnownObjectsBase<WGPUDevice>::Allocate(handle, state);
         AddToKnownSet(data);
         return data;
     }
diff --git a/src/dawn/wire/server/Server.cpp b/src/dawn/wire/server/Server.cpp
index 2796966..7de375f 100644
--- a/src/dawn/wire/server/Server.cpp
+++ b/src/dawn/wire/server/Server.cpp
@@ -54,7 +54,7 @@
         return false;
     }
 
-    ObjectData<WGPUTexture>* data = TextureObjects().Allocate(id);
+    ObjectData<WGPUTexture>* data = TextureObjects().Allocate(ObjectHandle{id, generation});
     if (data == nullptr) {
         return false;
     }
@@ -81,7 +81,7 @@
         return false;
     }
 
-    ObjectData<WGPUSwapChain>* data = SwapChainObjects().Allocate(id);
+    ObjectData<WGPUSwapChain>* data = SwapChainObjects().Allocate(ObjectHandle{id, generation});
     if (data == nullptr) {
         return false;
     }
@@ -99,7 +99,7 @@
 
 bool Server::InjectDevice(WGPUDevice device, uint32_t id, uint32_t generation) {
     ASSERT(device != nullptr);
-    ObjectData<WGPUDevice>* data = DeviceObjects().Allocate(id);
+    ObjectData<WGPUDevice>* data = DeviceObjects().Allocate(ObjectHandle{id, generation});
     if (data == nullptr) {
         return false;
     }
@@ -121,7 +121,7 @@
 
 bool Server::InjectInstance(WGPUInstance instance, uint32_t id, uint32_t generation) {
     ASSERT(instance != nullptr);
-    ObjectData<WGPUInstance>* data = InstanceObjects().Allocate(id);
+    ObjectData<WGPUInstance>* data = InstanceObjects().Allocate(ObjectHandle{id, generation});
     if (data == nullptr) {
         return false;
     }
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index 9735d26..8fa9e06 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -28,7 +28,7 @@
         return false;
     }
 
-    auto* resultData = DeviceObjects().Allocate(deviceHandle.id, AllocationState::Reserved);
+    auto* resultData = DeviceObjects().Allocate(deviceHandle, AllocationState::Reserved);
     if (resultData == nullptr) {
         return false;
     }
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 61d5e15..f07bdfa 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -110,7 +110,7 @@
     }
 
     // Create and register the buffer object.
-    auto* resultData = BufferObjects().Allocate(bufferResult.id);
+    auto* resultData = BufferObjects().Allocate(bufferResult);
     if (resultData == nullptr) {
         return false;
     }
diff --git a/src/dawn/wire/server/ServerDevice.cpp b/src/dawn/wire/server/ServerDevice.cpp
index 2797815..0281c33 100644
--- a/src/dawn/wire/server/ServerDevice.cpp
+++ b/src/dawn/wire/server/ServerDevice.cpp
@@ -99,7 +99,7 @@
     }
 
     auto* resultData =
-        ComputePipelineObjects().Allocate(pipelineObjectHandle.id, AllocationState::Reserved);
+        ComputePipelineObjects().Allocate(pipelineObjectHandle, AllocationState::Reserved);
     if (resultData == nullptr) {
         return false;
     }
@@ -143,7 +143,7 @@
     }
 
     auto* resultData =
-        RenderPipelineObjects().Allocate(pipelineObjectHandle.id, AllocationState::Reserved);
+        RenderPipelineObjects().Allocate(pipelineObjectHandle, AllocationState::Reserved);
     if (resultData == nullptr) {
         return false;
     }
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index dcdf62c..4357604 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -29,7 +29,7 @@
         return false;
     }
 
-    auto* resultData = AdapterObjects().Allocate(adapterHandle.id, AllocationState::Reserved);
+    auto* resultData = AdapterObjects().Allocate(adapterHandle, AllocationState::Reserved);
     if (resultData == nullptr) {
         return false;
     }