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 <cwallez@chromium.org>
Reviewed-by: Jiawei Shao <jiawei.shao@intel.com>
Commit-Queue: Austin Eng <enga@chromium.org>
This commit is contained in:
Austin Eng 2020-11-13 01:49:12 +00:00 committed by Commit Bot service account
parent 8d69298d9b
commit bc6a700bf5
2 changed files with 38 additions and 21 deletions

View File

@ -28,39 +28,56 @@ namespace dawn_wire {
// in-flight chunked command, and then pass the rest along to a second call to // in-flight chunked command, and then pass the rest along to a second call to
// |HandleCommandsImpl|. // |HandleCommandsImpl|.
size_t chunkSize = std::min(size, mChunkedCommandRemainingSize); size_t chunkSize = std::min(size, mChunkedCommandRemainingSize);
mChunkedCommandData.insert(mChunkedCommandData.end(), commands, commands + chunkSize);
memcpy(mChunkedCommandData.get() + mChunkedCommandPutOffset,
const_cast<const char*>(commands), chunkSize);
mChunkedCommandPutOffset += chunkSize;
mChunkedCommandRemainingSize -= chunkSize;
commands += chunkSize; commands += chunkSize;
mChunkedCommandRemainingSize -= chunkSize;
size -= chunkSize; size -= chunkSize;
if (mChunkedCommandRemainingSize == 0) { if (mChunkedCommandRemainingSize == 0) {
// Once the chunked command is complete, pass the data to the command handler // Once the chunked command is complete, pass the data to the command handler
// implemenation. // implemenation.
const char* chunkedCommands = mChunkedCommandData.data(); auto chunkedCommandData = std::move(mChunkedCommandData);
size_t chunkedSize = mChunkedCommandData.size(); if (HandleCommandsImpl(chunkedCommandData.get(), mChunkedCommandPutOffset) ==
if (HandleCommandsImpl(chunkedCommands, chunkedSize) == nullptr) { nullptr) {
// |HandleCommandsImpl| returns nullptr on error. Forward any errors // |HandleCommandsImpl| returns nullptr on error. Forward any errors
// out. // out.
return nullptr; return nullptr;
} }
mChunkedCommandData.clear();
} }
} }
return HandleCommandsImpl(commands, size); return HandleCommandsImpl(commands, size);
} }
void ChunkedCommandHandler::BeginChunkedCommandData(const volatile char* commands, ChunkedCommandHandler::ChunkedCommandsResult ChunkedCommandHandler::BeginChunkedCommandData(
size_t commandSize, const volatile char* commands,
size_t initialSize) { size_t commandSize,
ASSERT(mChunkedCommandData.empty()); size_t initialSize) {
ASSERT(!mChunkedCommandData);
// Reserve space for all the command data we're expecting, and append the initial data #if defined(ADDRESS_SANITIZER)
// to the end of the vector. 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<const char*>(commands), initialSize);
mChunkedCommandPutOffset = initialSize;
mChunkedCommandRemainingSize = commandSize - initialSize; mChunkedCommandRemainingSize = commandSize - initialSize;
mChunkedCommandData.reserve(commandSize);
mChunkedCommandData.insert(mChunkedCommandData.end(), commands, commands + initialSize); return ChunkedCommandsResult::Consumed;
} }
} // namespace dawn_wire } // namespace dawn_wire

View File

@ -20,7 +20,7 @@
#include "dawn_wire/WireCmd_autogen.h" #include "dawn_wire/WireCmd_autogen.h"
#include <cstdint> #include <cstdint>
#include <vector> #include <memory>
namespace dawn_wire { namespace dawn_wire {
@ -48,8 +48,7 @@ namespace dawn_wire {
} }
size_t commandSize = static_cast<size_t>(commandSize64); size_t commandSize = static_cast<size_t>(commandSize64);
if (size < commandSize) { if (size < commandSize) {
BeginChunkedCommandData(commands, commandSize, size); return BeginChunkedCommandData(commands, commandSize, size);
return ChunkedCommandsResult::Consumed;
} }
return ChunkedCommandsResult::Passthrough; return ChunkedCommandsResult::Passthrough;
} }
@ -58,12 +57,13 @@ namespace dawn_wire {
virtual const volatile char* HandleCommandsImpl(const volatile char* commands, virtual const volatile char* HandleCommandsImpl(const volatile char* commands,
size_t size) = 0; size_t size) = 0;
void BeginChunkedCommandData(const volatile char* commands, ChunkedCommandsResult BeginChunkedCommandData(const volatile char* commands,
size_t commandSize, size_t commandSize,
size_t initialSize); size_t initialSize);
size_t mChunkedCommandRemainingSize = 0; size_t mChunkedCommandRemainingSize = 0;
std::vector<char> mChunkedCommandData; size_t mChunkedCommandPutOffset = 0;
std::unique_ptr<char[]> mChunkedCommandData;
}; };
} // namespace dawn_wire } // namespace dawn_wire