From bc6a700bf51a05844497e3af689772cf0913f18a Mon Sep 17 00:00:00 2001 From: Austin Eng Date: Fri, 13 Nov 2020 01:49:12 +0000 Subject: [PATCH] dawn_wire: Protect against large allocations in ChunkedCommandHandler Use std::nothrow on the allocation to catch failed allocations. Enforce a max allocation limit a bit lower than ASAN's max 2GB allocation. Bug: chromium:1145204 Change-Id: I91f2ddd5b58da6c39d4ab8bc447f7d9b7af8615f Reviewed-on: https://dawn-review.googlesource.com/c/dawn/+/32340 Reviewed-by: Corentin Wallez Reviewed-by: Jiawei Shao Commit-Queue: Austin Eng --- src/dawn_wire/ChunkedCommandHandler.cpp | 45 +++++++++++++++++-------- src/dawn_wire/ChunkedCommandHandler.h | 14 ++++---- 2 files changed, 38 insertions(+), 21 deletions(-) diff --git a/src/dawn_wire/ChunkedCommandHandler.cpp b/src/dawn_wire/ChunkedCommandHandler.cpp index a2d6add296..e91e540fa5 100644 --- a/src/dawn_wire/ChunkedCommandHandler.cpp +++ b/src/dawn_wire/ChunkedCommandHandler.cpp @@ -28,39 +28,56 @@ namespace dawn_wire { // in-flight chunked command, and then pass the rest along to a second call to // |HandleCommandsImpl|. size_t chunkSize = std::min(size, mChunkedCommandRemainingSize); - mChunkedCommandData.insert(mChunkedCommandData.end(), commands, commands + chunkSize); + + memcpy(mChunkedCommandData.get() + mChunkedCommandPutOffset, + const_cast(commands), chunkSize); + mChunkedCommandPutOffset += chunkSize; + mChunkedCommandRemainingSize -= chunkSize; commands += chunkSize; - mChunkedCommandRemainingSize -= chunkSize; size -= chunkSize; if (mChunkedCommandRemainingSize == 0) { // Once the chunked command is complete, pass the data to the command handler // implemenation. - const char* chunkedCommands = mChunkedCommandData.data(); - size_t chunkedSize = mChunkedCommandData.size(); - if (HandleCommandsImpl(chunkedCommands, chunkedSize) == nullptr) { + auto chunkedCommandData = std::move(mChunkedCommandData); + if (HandleCommandsImpl(chunkedCommandData.get(), mChunkedCommandPutOffset) == + nullptr) { // |HandleCommandsImpl| returns nullptr on error. Forward any errors // out. return nullptr; } - mChunkedCommandData.clear(); } } return HandleCommandsImpl(commands, size); } - void ChunkedCommandHandler::BeginChunkedCommandData(const volatile char* commands, - size_t commandSize, - size_t initialSize) { - ASSERT(mChunkedCommandData.empty()); + ChunkedCommandHandler::ChunkedCommandsResult ChunkedCommandHandler::BeginChunkedCommandData( + const volatile char* commands, + size_t commandSize, + size_t initialSize) { + ASSERT(!mChunkedCommandData); - // Reserve space for all the command data we're expecting, and append the initial data - // to the end of the vector. +#if defined(ADDRESS_SANITIZER) + if (commandSize >= 0x70000000) { + // std::nothrow isn't implemented on ASAN and it has a 2GB allocation limit. + // Catch large allocations and error out so fuzzers make progress. + return ChunkedCommandsResult::Error; + } +#endif + // Reserve space for all the command data we're expecting, and copy the initial data + // to the start of the memory. + mChunkedCommandData.reset(new (std::nothrow) char[commandSize]); + if (!mChunkedCommandData) { + return ChunkedCommandsResult::Error; + } + + memcpy(mChunkedCommandData.get(), const_cast(commands), initialSize); + mChunkedCommandPutOffset = initialSize; mChunkedCommandRemainingSize = commandSize - initialSize; - mChunkedCommandData.reserve(commandSize); - mChunkedCommandData.insert(mChunkedCommandData.end(), commands, commands + initialSize); + + return ChunkedCommandsResult::Consumed; } } // namespace dawn_wire diff --git a/src/dawn_wire/ChunkedCommandHandler.h b/src/dawn_wire/ChunkedCommandHandler.h index 3f397eda50..182ceadecb 100644 --- a/src/dawn_wire/ChunkedCommandHandler.h +++ b/src/dawn_wire/ChunkedCommandHandler.h @@ -20,7 +20,7 @@ #include "dawn_wire/WireCmd_autogen.h" #include -#include +#include namespace dawn_wire { @@ -48,8 +48,7 @@ namespace dawn_wire { } size_t commandSize = static_cast(commandSize64); if (size < commandSize) { - BeginChunkedCommandData(commands, commandSize, size); - return ChunkedCommandsResult::Consumed; + return BeginChunkedCommandData(commands, commandSize, size); } return ChunkedCommandsResult::Passthrough; } @@ -58,12 +57,13 @@ namespace dawn_wire { virtual const volatile char* HandleCommandsImpl(const volatile char* commands, size_t size) = 0; - void BeginChunkedCommandData(const volatile char* commands, - size_t commandSize, - size_t initialSize); + ChunkedCommandsResult BeginChunkedCommandData(const volatile char* commands, + size_t commandSize, + size_t initialSize); size_t mChunkedCommandRemainingSize = 0; - std::vector mChunkedCommandData; + size_t mChunkedCommandPutOffset = 0; + std::unique_ptr mChunkedCommandData; }; } // namespace dawn_wire