[dawn][wire] Introduce Reserved objects for allocation and release.

- This differentiates Known and Reserved objects. This is important
  so that we properly release objects, i.e. Devices, that may be
  reserved but never fulfilled on the Server side.
- Fixes the fuzzer bug by ensuring that trying to create children
  object with a bad/non-existent backend-backed Device does not
  cause a bad memory read when trying to access a nullptr.

Bug: b:333756600 b:333641451
Change-Id: Idd0f754c46d265d45e4d89a96f4756b982bcd746
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/183422
Commit-Queue: Loko Kung <lokokung@google.com>
Reviewed-by: Austin Eng <enga@chromium.org>
diff --git a/generator/templates/dawn/wire/server/ServerDoers.cpp b/generator/templates/dawn/wire/server/ServerDoers.cpp
index 4431f14..8f05ea4 100644
--- a/generator/templates/dawn/wire/server/ServerDoers.cpp
+++ b/generator/templates/dawn/wire/server/ServerDoers.cpp
@@ -81,21 +81,17 @@
         switch(objectType) {
             {% for type in by_category["object"] %}
                 case ObjectType::{{type.name.CamelCase()}}: {
-                    Known<WGPU{{type.name.CamelCase()}}> obj;
+                    Reserved<WGPU{{type.name.CamelCase()}}> obj;
                     WIRE_TRY({{type.name.CamelCase()}}Objects().Get(objectId, &obj));
 
                     if (obj->state == AllocationState::Allocated) {
+                        DAWN_ASSERT(obj->handle != nullptr);
                         {% if type.name.get() == "device" %}
-                            if (obj->handle != nullptr) {
-                                //* Deregisters uncaptured error and device lost callbacks since
-                                //* they should not be forwarded if the device no longer exists on the wire.
-                                ClearDeviceCallbacks(obj->handle);
-                                mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle);
-                            }
-                        {% else %}
-                            DAWN_ASSERT(obj->handle != nullptr);
-                            mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle);
+                            //* Deregisters uncaptured error and device lost callbacks since
+                            //* they should not be forwarded if the device no longer exists on the wire.
+                            ClearDeviceCallbacks(obj->handle);
                         {% endif %}
+                        mProcs.{{as_varName(type.name, Name("release"))}}(obj->handle);
                     }
                     {{type.name.CamelCase()}}Objects().Free(objectId);
                     return WireResult::Success;
diff --git a/generator/templates/dawn/wire/server/ServerHandlers.cpp b/generator/templates/dawn/wire/server/ServerHandlers.cpp
index b6624b6..7b8110b 100644
--- a/generator/templates/dawn/wire/server/ServerHandlers.cpp
+++ b/generator/templates/dawn/wire/server/ServerHandlers.cpp
@@ -52,7 +52,7 @@
                 {{ assert(member.handle_type) }}
                 {% set Type = member.handle_type.name.CamelCase() %}
                 {% set name = as_varName(member.name) %}
-                Known<WGPU{{Type}}> {{name}}Data;
+                Reserved<WGPU{{Type}}> {{name}}Data;
                 WIRE_TRY({{Type}}Objects().Allocate(&{{name}}Data, cmd.{{name}}));
                 {{name}}Data->generation = cmd.{{name}}.generation;
             {%- endfor %}
diff --git a/src/dawn/wire/server/ObjectStorage.h b/src/dawn/wire/server/ObjectStorage.h
index 797499a..5ad842a 100644
--- a/src/dawn/wire/server/ObjectStorage.h
+++ b/src/dawn/wire/server/ObjectStorage.h
@@ -52,7 +52,7 @@
 template <typename T>
 struct ObjectDataBase {
     // The backend-provided handle and generation to this object.
-    T handle;
+    T handle = nullptr;
     ObjectGeneration generation = 0;
 
     AllocationState state;
@@ -87,9 +87,10 @@
     std::unique_ptr<DeviceInfo> info = std::make_unique<DeviceInfo>();
 };
 
-// Information of both an ID and an object data for use as a shorthand in doers.
+// Information of both an ID and an object data for use as a shorthand in doers. Reserved objects
+// are guaranteed to have been reserved, but not guaranteed to be backed by a valid backend handle.
 template <typename T>
-struct Known {
+struct Reserved {
     ObjectId id;
     raw_ptr<ObjectData<T>> data;
 
@@ -108,6 +109,31 @@
     }
 };
 
+// Information of both an ID and an object data for use as a shorthand in doers. Known objects are
+// guaranteed to be backed by a valid backend handle.
+template <typename T>
+struct Known {
+    ObjectId id;
+    raw_ptr<ObjectData<T>> data;
+
+    const ObjectData<T>* operator->() const {
+        DAWN_ASSERT(data != nullptr);
+        DAWN_ASSERT(data->state == AllocationState::Allocated);
+        return data;
+    }
+    ObjectData<T>* operator->() {
+        DAWN_ASSERT(data != nullptr);
+        DAWN_ASSERT(data->state == AllocationState::Allocated);
+        return data;
+    }
+
+    ObjectHandle AsHandle() const {
+        DAWN_ASSERT(data != nullptr);
+        DAWN_ASSERT(data->state == AllocationState::Allocated);
+        return {id, data->generation};
+    }
+};
+
 // Keeps track of the mapping between client IDs and backend objects.
 template <typename T>
 class KnownObjectsBase {
@@ -140,6 +166,20 @@
         return WireResult::Success;
     }
 
+    WireResult Get(ObjectId id, Reserved<T>* result) {
+        if (id >= mKnown.size()) {
+            return WireResult::FatalError;
+        }
+
+        Data* data = &mKnown[id];
+        if (data->state == AllocationState::Free) {
+            return WireResult::FatalError;
+        }
+
+        *result = Reserved<T>{id, data};
+        return WireResult::Success;
+    }
+
     WireResult Get(ObjectId id, Known<T>* result) {
         if (id >= mKnown.size()) {
             return WireResult::FatalError;
@@ -166,7 +206,7 @@
     // Allocates the data for a given ID and returns it in result.
     // Returns false 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*
-    WireResult Allocate(Known<T>* result,
+    WireResult Allocate(Reserved<T>* result,
                         ObjectHandle handle,
                         AllocationState state = AllocationState::Allocated) {
         if (handle.id == 0 || handle.id > mKnown.size()) {
@@ -245,17 +285,16 @@
   public:
     KnownObjects() = default;
 
-    WireResult Allocate(Known<WGPUDevice>* result,
+    WireResult Allocate(Reserved<WGPUDevice>* result,
                         ObjectHandle handle,
                         AllocationState state = AllocationState::Allocated) {
         WIRE_TRY(KnownObjectsBase<WGPUDevice>::Allocate(result, handle, state));
-        AddToKnownSet(*result);
         return WireResult::Success;
     }
 
     Known<WGPUDevice> FillReservation(ObjectId id, WGPUDevice handle) {
         Known<WGPUDevice> result = KnownObjectsBase<WGPUDevice>::FillReservation(id, handle);
-        AddToKnownSet(result);
+        mKnownSet.insert(result->handle);
         return result;
     }
 
@@ -267,11 +306,6 @@
     bool IsKnown(WGPUDevice device) const { return mKnownSet.contains(device); }
 
   private:
-    void AddToKnownSet(Known<WGPUDevice> device) {
-        if (device->state == AllocationState::Allocated && device->handle != nullptr) {
-            mKnownSet.insert(device->handle);
-        }
-    }
     absl::flat_hash_set<WGPUDevice> mKnownSet;
 };
 
diff --git a/src/dawn/wire/server/Server.cpp b/src/dawn/wire/server/Server.cpp
index 7ed991e..bac95a1 100644
--- a/src/dawn/wire/server/Server.cpp
+++ b/src/dawn/wire/server/Server.cpp
@@ -71,7 +71,7 @@
         return WireResult::FatalError;
     }
 
-    Known<WGPUTexture> data;
+    Reserved<WGPUTexture> data;
     WIRE_TRY(TextureObjects().Allocate(&data, handle));
 
     data->handle = texture;
@@ -95,7 +95,7 @@
         return WireResult::FatalError;
     }
 
-    Known<WGPUSwapChain> data;
+    Reserved<WGPUSwapChain> data;
     WIRE_TRY(SwapChainObjects().Allocate(&data, handle));
 
     data->handle = swapchain;
@@ -111,7 +111,7 @@
 
 WireResult Server::InjectInstance(WGPUInstance instance, const Handle& handle) {
     DAWN_ASSERT(instance != nullptr);
-    Known<WGPUInstance> data;
+    Reserved<WGPUInstance> data;
     WIRE_TRY(InstanceObjects().Allocate(&data, handle));
 
     data->handle = instance;
diff --git a/src/dawn/wire/server/ServerAdapter.cpp b/src/dawn/wire/server/ServerAdapter.cpp
index c5bbba1..4eb4c93 100644
--- a/src/dawn/wire/server/ServerAdapter.cpp
+++ b/src/dawn/wire/server/ServerAdapter.cpp
@@ -38,7 +38,7 @@
                                           ObjectHandle deviceHandle,
                                           WGPUFuture deviceLostFuture,
                                           const WGPUDeviceDescriptor* descriptor) {
-    Known<WGPUDevice> device;
+    Reserved<WGPUDevice> device;
     WIRE_TRY(DeviceObjects().Allocate(&device, deviceHandle, AllocationState::Reserved));
 
     auto userdata = MakeUserdata<RequestDeviceUserdata>();
@@ -73,19 +73,9 @@
     cmd.status = status;
     cmd.message = message;
 
-    // We always fill the reservation once we complete so that the client is the one to release it.
-    auto FillReservation = [&]() {
-        Known<WGPUDevice> reservation =
-            DeviceObjects().FillReservation(data->deviceObjectId, device);
-        reservation->info->server = this;
-        reservation->info->self = reservation.AsHandle();
-        SerializeCommand(cmd);
-        return reservation;
-    };
-
     if (status != WGPURequestDeviceStatus_Success) {
         DAWN_ASSERT(device == nullptr);
-        FillReservation();
+        SerializeCommand(cmd);
         return;
     }
 
@@ -107,7 +97,7 @@
 
             cmd.status = WGPURequestDeviceStatus_Error;
             cmd.message = "Requested feature not supported.";
-            FillReservation();
+            SerializeCommand(cmd);
             return;
         }
     }
@@ -124,9 +114,12 @@
     cmd.limits = &limits;
 
     // Assign the handle and allocated status if the device is created successfully.
-    Known<WGPUDevice> reservation = FillReservation();
+    Known<WGPUDevice> reservation = DeviceObjects().FillReservation(data->deviceObjectId, device);
     DAWN_ASSERT(reservation.data != nullptr);
+    reservation->info->server = this;
+    reservation->info->self = reservation.AsHandle();
     SetForwardingDeviceCallbacks(reservation);
+    SerializeCommand(cmd);
 }
 
 }  // namespace dawn::wire::server
diff --git a/src/dawn/wire/server/ServerBuffer.cpp b/src/dawn/wire/server/ServerBuffer.cpp
index 22e2769..56be933 100644
--- a/src/dawn/wire/server/ServerBuffer.cpp
+++ b/src/dawn/wire/server/ServerBuffer.cpp
@@ -113,7 +113,7 @@
                                         uint64_t writeHandleCreateInfoLength,
                                         const uint8_t* writeHandleCreateInfo) {
     // Create and register the buffer object.
-    Known<WGPUBuffer> buffer;
+    Reserved<WGPUBuffer> buffer;
     WIRE_TRY(BufferObjects().Allocate(&buffer, bufferHandle));
     buffer->handle = mProcs.deviceCreateBuffer(device->handle, descriptor);
     buffer->usage = descriptor->usage;
diff --git a/src/dawn/wire/server/ServerDevice.cpp b/src/dawn/wire/server/ServerDevice.cpp
index f9fa66b..8c20356 100644
--- a/src/dawn/wire/server/ServerDevice.cpp
+++ b/src/dawn/wire/server/ServerDevice.cpp
@@ -109,7 +109,7 @@
     WGPUFuture future,
     ObjectHandle pipelineObjectHandle,
     const WGPUComputePipelineDescriptor* descriptor) {
-    Known<WGPUComputePipeline> pipeline;
+    Reserved<WGPUComputePipeline> pipeline;
     WIRE_TRY(ComputePipelineObjects().Allocate(&pipeline, pipelineObjectHandle,
                                                AllocationState::Reserved));
 
@@ -147,7 +147,7 @@
     WGPUFuture future,
     ObjectHandle pipelineObjectHandle,
     const WGPURenderPipelineDescriptor* descriptor) {
-    Known<WGPURenderPipeline> pipeline;
+    Reserved<WGPURenderPipeline> pipeline;
     WIRE_TRY(RenderPipelineObjects().Allocate(&pipeline, pipelineObjectHandle,
                                               AllocationState::Reserved));
 
diff --git a/src/dawn/wire/server/ServerInstance.cpp b/src/dawn/wire/server/ServerInstance.cpp
index c25688e..255ab3f 100644
--- a/src/dawn/wire/server/ServerInstance.cpp
+++ b/src/dawn/wire/server/ServerInstance.cpp
@@ -38,7 +38,7 @@
                                             WGPUFuture future,
                                             ObjectHandle adapterHandle,
                                             const WGPURequestAdapterOptions* options) {
-    Known<WGPUAdapter> adapter;
+    Reserved<WGPUAdapter> adapter;
     WIRE_TRY(AdapterObjects().Allocate(&adapter, adapterHandle, AllocationState::Reserved));
 
     auto userdata = MakeUserdata<RequestAdapterUserdata>();