Add a BufferConsumer primitive for wire [de]serialization

BufferConsumer wraps a buffer pointer and size and exposes a
limited number of operations to get data while decrementing
the remaining available size. This makes it so that code
reading or writing into a buffer cannot easily consume more
bytes than available.

This CL guards against serialization overflows using
BufferConsumer, and it implements GetPtrFromBuffer
(for deserialization) on top of BufferConsumer. A future patch
will make the rest of the deserialization code use BufferConsumer.

Bug: dawn:680
Change-Id: Ic2bd6e7039e83ce70307c2ff47aaca9891c16d91
Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/41780
Commit-Queue: Austin Eng <enga@chromium.org>
Reviewed-by: Stephen White <senorblanco@chromium.org>
diff --git a/src/dawn_wire/BUILD.gn b/src/dawn_wire/BUILD.gn
index 04340b0..4b21299 100644
--- a/src/dawn_wire/BUILD.gn
+++ b/src/dawn_wire/BUILD.gn
@@ -63,6 +63,7 @@
     "ChunkedCommandHandler.h",
     "ChunkedCommandSerializer.cpp",
     "ChunkedCommandSerializer.h",
+    "Wire.cpp",
     "WireClient.cpp",
     "WireDeserializeAllocator.cpp",
     "WireDeserializeAllocator.h",
diff --git a/src/dawn_wire/CMakeLists.txt b/src/dawn_wire/CMakeLists.txt
index 2776bb4..d6d430f 100644
--- a/src/dawn_wire/CMakeLists.txt
+++ b/src/dawn_wire/CMakeLists.txt
@@ -35,6 +35,7 @@
     "ChunkedCommandHandler.h"
     "ChunkedCommandSerializer.cpp"
     "ChunkedCommandSerializer.h"
+    "Wire.cpp"
     "WireClient.cpp"
     "WireDeserializeAllocator.cpp"
     "WireDeserializeAllocator.h"
diff --git a/src/dawn_wire/ChunkedCommandSerializer.h b/src/dawn_wire/ChunkedCommandSerializer.h
index 1f21dcd..e62cb99 100644
--- a/src/dawn_wire/ChunkedCommandSerializer.h
+++ b/src/dawn_wire/ChunkedCommandSerializer.h
@@ -32,7 +32,7 @@
 
         template <typename Cmd>
         void SerializeCommand(const Cmd& cmd) {
-            SerializeCommand(cmd, 0, [](char*) {});
+            SerializeCommand(cmd, 0, [](SerializeBuffer*) { return true; });
         }
 
         template <typename Cmd, typename ExtraSizeSerializeFn>
@@ -41,15 +41,15 @@
                               ExtraSizeSerializeFn&& SerializeExtraSize) {
             SerializeCommandImpl(
                 cmd,
-                [](const Cmd& cmd, size_t requiredSize, char* allocatedBuffer) {
-                    cmd.Serialize(requiredSize, allocatedBuffer);
+                [](const Cmd& cmd, size_t requiredSize, SerializeBuffer* serializeBuffer) {
+                    return cmd.Serialize(requiredSize, serializeBuffer);
                 },
                 extraSize, std::forward<ExtraSizeSerializeFn>(SerializeExtraSize));
         }
 
         template <typename Cmd>
         void SerializeCommand(const Cmd& cmd, const ObjectIdProvider& objectIdProvider) {
-            SerializeCommand(cmd, objectIdProvider, 0, [](char*) {});
+            SerializeCommand(cmd, objectIdProvider, 0, [](SerializeBuffer*) { return true; });
         }
 
         template <typename Cmd, typename ExtraSizeSerializeFn>
@@ -59,8 +59,9 @@
                               ExtraSizeSerializeFn&& SerializeExtraSize) {
             SerializeCommandImpl(
                 cmd,
-                [&objectIdProvider](const Cmd& cmd, size_t requiredSize, char* allocatedBuffer) {
-                    cmd.Serialize(requiredSize, allocatedBuffer, objectIdProvider);
+                [&objectIdProvider](const Cmd& cmd, size_t requiredSize,
+                                    SerializeBuffer* serializeBuffer) {
+                    return cmd.Serialize(requiredSize, serializeBuffer, objectIdProvider);
                 },
                 extraSize, std::forward<ExtraSizeSerializeFn>(SerializeExtraSize));
         }
@@ -77,8 +78,13 @@
             if (requiredSize <= mMaxAllocationSize) {
                 char* allocatedBuffer = static_cast<char*>(mSerializer->GetCmdSpace(requiredSize));
                 if (allocatedBuffer != nullptr) {
-                    SerializeCmd(cmd, requiredSize, allocatedBuffer);
-                    SerializeExtraSize(allocatedBuffer + commandSize);
+                    SerializeBuffer serializeBuffer(allocatedBuffer, requiredSize);
+                    bool success = true;
+                    success &= SerializeCmd(cmd, requiredSize, &serializeBuffer);
+                    success &= SerializeExtraSize(&serializeBuffer);
+                    if (DAWN_UNLIKELY(!success)) {
+                        mSerializer->OnSerializeError();
+                    }
                 }
                 return;
             }
@@ -87,8 +93,14 @@
             if (!cmdSpace) {
                 return;
             }
-            SerializeCmd(cmd, requiredSize, cmdSpace.get());
-            SerializeExtraSize(cmdSpace.get() + commandSize);
+            SerializeBuffer serializeBuffer(cmdSpace.get(), requiredSize);
+            bool success = true;
+            success &= SerializeCmd(cmd, requiredSize, &serializeBuffer);
+            success &= SerializeExtraSize(&serializeBuffer);
+            if (DAWN_UNLIKELY(!success)) {
+                mSerializer->OnSerializeError();
+                return;
+            }
             SerializeChunkedCommand(cmdSpace.get(), requiredSize);
         }
 
diff --git a/src/dawn_wire/Wire.cpp b/src/dawn_wire/Wire.cpp
new file mode 100644
index 0000000..89e5ac1
--- /dev/null
+++ b/src/dawn_wire/Wire.cpp
@@ -0,0 +1,26 @@
+// Copyright 2021 The Dawn Authors
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+#include "dawn_wire/Wire.h"
+
+namespace dawn_wire {
+
+    CommandSerializer::~CommandSerializer() = default;
+
+    void CommandSerializer::OnSerializeError() {
+    }
+
+    CommandHandler::~CommandHandler() = default;
+
+}  // namespace dawn_wire
diff --git a/src/dawn_wire/client/Buffer.cpp b/src/dawn_wire/client/Buffer.cpp
index da3691c..4f45984 100644
--- a/src/dawn_wire/client/Buffer.cpp
+++ b/src/dawn_wire/client/Buffer.cpp
@@ -14,6 +14,7 @@
 
 #include "dawn_wire/client/Buffer.h"
 
+#include "dawn_wire/WireCmd_autogen.h"
 #include "dawn_wire/client/Client.h"
 #include "dawn_wire/client/Device.h"
 
@@ -73,19 +74,24 @@
         cmd.handleCreateInfoLength = writeHandleCreateInfoLength;
         cmd.handleCreateInfo = nullptr;
 
-        wireClient->SerializeCommand(cmd, writeHandleCreateInfoLength, [&](char* cmdSpace) {
-            if (descriptor->mappedAtCreation) {
-                // Serialize the WriteHandle into the space after the command.
-                writeHandle->SerializeCreate(cmdSpace);
+        wireClient->SerializeCommand(
+            cmd, writeHandleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
+                if (descriptor->mappedAtCreation) {
+                    if (serializeBuffer->AvailableSize() != writeHandleCreateInfoLength) {
+                        return false;
+                    }
+                    // Serialize the WriteHandle into the space after the command.
+                    writeHandle->SerializeCreate(serializeBuffer->Buffer());
 
-                // Set the buffer state for the mapping at creation. The buffer now owns the write
-                // handle..
-                buffer->mWriteHandle = std::move(writeHandle);
-                buffer->mMappedData = writeData;
-                buffer->mMapOffset = 0;
-                buffer->mMapSize = buffer->mSize;
-            }
-        });
+                    // Set the buffer state for the mapping at creation. The buffer now owns the
+                    // write handle..
+                    buffer->mWriteHandle = std::move(writeHandle);
+                    buffer->mMappedData = writeData;
+                    buffer->mMapOffset = 0;
+                    buffer->mMapSize = buffer->mSize;
+                }
+                return true;
+            });
         return ToAPI(buffer);
     }
 
@@ -199,15 +205,25 @@
         // Step 3a. Fill the handle create info in the command.
         if (isReadMode) {
             cmd.handleCreateInfoLength = request.readHandle->SerializeCreateSize();
-            client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
-                request.readHandle->SerializeCreate(cmdSpace);
-            });
+            client->SerializeCommand(
+                cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
+                    bool success = serializeBuffer->AvailableSize() == cmd.handleCreateInfoLength;
+                    if (success) {
+                        request.readHandle->SerializeCreate(serializeBuffer->Buffer());
+                    }
+                    return success;
+                });
         } else {
             ASSERT(isWriteMode);
             cmd.handleCreateInfoLength = request.writeHandle->SerializeCreateSize();
-            client->SerializeCommand(cmd, cmd.handleCreateInfoLength, [&](char* cmdSpace) {
-                request.writeHandle->SerializeCreate(cmdSpace);
-            });
+            client->SerializeCommand(
+                cmd, cmd.handleCreateInfoLength, [&](SerializeBuffer* serializeBuffer) {
+                    bool success = serializeBuffer->AvailableSize() == cmd.handleCreateInfoLength;
+                    if (success) {
+                        request.writeHandle->SerializeCreate(serializeBuffer->Buffer());
+                    }
+                    return success;
+                });
         }
 
         // Step 4. Register this request so that we can retrieve it from its serial when the server
@@ -334,11 +350,16 @@
             cmd.writeFlushInfoLength = writeFlushInfoLength;
             cmd.writeFlushInfo = nullptr;
 
-            client->SerializeCommand(cmd, writeFlushInfoLength, [&](char* cmdSpace) {
-                // Serialize flush metadata into the space after the command.
-                // This closes the handle for writing.
-                mWriteHandle->SerializeFlush(cmdSpace);
-            });
+            client->SerializeCommand(
+                cmd, writeFlushInfoLength, [&](SerializeBuffer* serializeBuffer) {
+                    bool success = serializeBuffer->AvailableSize() == writeFlushInfoLength;
+                    if (success) {
+                        // Serialize flush metadata into the space after the command.
+                        // This closes the handle for writing.
+                        mWriteHandle->SerializeFlush(serializeBuffer->Buffer());
+                    }
+                    return success;
+                });
             mWriteHandle = nullptr;
 
         } else if (mReadHandle) {
diff --git a/src/dawn_wire/server/ServerBuffer.cpp b/src/dawn_wire/server/ServerBuffer.cpp
index 7cc5c9b..c279845 100644
--- a/src/dawn_wire/server/ServerBuffer.cpp
+++ b/src/dawn_wire/server/ServerBuffer.cpp
@@ -13,6 +13,7 @@
 // limitations under the License.
 
 #include "common/Assert.h"
+#include "dawn_wire/WireCmd_autogen.h"
 #include "dawn_wire/server/Server.h"
 
 #include <memory>
@@ -242,11 +243,15 @@
                 data->readHandle->SerializeInitialDataSize(readData, data->size);
         }
 
-        SerializeCommand(cmd, cmd.readInitialDataInfoLength, [&](char* cmdSpace) {
+        SerializeCommand(cmd, cmd.readInitialDataInfoLength, [&](SerializeBuffer* serializeBuffer) {
             if (isSuccess) {
                 if (isRead) {
+                    if (serializeBuffer->AvailableSize() != cmd.readInitialDataInfoLength) {
+                        return false;
+                    }
                     // Serialize the initialization message into the space after the command.
-                    data->readHandle->SerializeInitialData(readData, data->size, cmdSpace);
+                    data->readHandle->SerializeInitialData(readData, data->size,
+                                                           serializeBuffer->Buffer());
                     // The in-flight map request returned successfully.
                     // Move the ReadHandle so it is owned by the buffer.
                     bufferData->readHandle = std::move(data->readHandle);
@@ -261,6 +266,7 @@
                         data->size);
                 }
             }
+            return true;
         });
     }
 
diff --git a/src/include/dawn_wire/Wire.h b/src/include/dawn_wire/Wire.h
index bb46701..590a6cc 100644
--- a/src/include/dawn_wire/Wire.h
+++ b/src/include/dawn_wire/Wire.h
@@ -25,7 +25,7 @@
 
     class DAWN_WIRE_EXPORT CommandSerializer {
       public:
-        virtual ~CommandSerializer() = default;
+        virtual ~CommandSerializer();
 
         // Get space for serializing commands.
         // GetCmdSpace will never be called with a value larger than
@@ -34,11 +34,12 @@
         virtual void* GetCmdSpace(size_t size) = 0;
         virtual bool Flush() = 0;
         virtual size_t GetMaximumAllocationSize() const = 0;
+        virtual void OnSerializeError();
     };
 
     class DAWN_WIRE_EXPORT CommandHandler {
       public:
-        virtual ~CommandHandler() = default;
+        virtual ~CommandHandler();
         virtual const volatile char* HandleCommands(const volatile char* commands, size_t size) = 0;
     };