From ddb770f0e4eb5cec88a8124a29c318b5e925bffe Mon Sep 17 00:00:00 2001 From: Michael Butler Date: Thu, 2 May 2019 18:16:13 -0700 Subject: [PATCH] NNAPI: validate that FmqResultDatum padding is 0 -- VTS FmqResultDatum::OperandInformation has padding that may not be initialized by an NN HAL service instance. This CL adds a validation check to ensure that services are not leaking uninitialized data through this padding region. Bug: 131356202 Test: mma Test: atest VtsHalNeuralnetworksV1_2TargetTest (for ValidationTest with sample-all) Test: altered sample-driver to randomly set a padding byte to 1; the new validation test successfully failed the test Change-Id: I6661945392b3fc773493d8f2f306f29b39e09bab --- .../1.2/vts/functional/ValidateBurst.cpp | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp index 386c141f80..8bb4934833 100644 --- a/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp +++ b/neuralnetworks/1.2/vts/functional/ValidateBurst.cpp @@ -25,6 +25,7 @@ #include "Utils.h" #include +#include namespace android { namespace hardware { @@ -317,12 +318,91 @@ static void validateBurstFmqLength(const sp& preparedModel, } } +static bool isSanitized(const FmqResultDatum& datum) { + using Discriminator = FmqResultDatum::hidl_discriminator; + + // check to ensure the padding values in the returned + // FmqResultDatum::OperandInformation are initialized to 0 + if (datum.getDiscriminator() == Discriminator::operandInformation) { + static_assert( + offsetof(FmqResultDatum::OperandInformation, isSufficient) == 0, + "unexpected value for offset of FmqResultDatum::OperandInformation::isSufficient"); + static_assert( + sizeof(FmqResultDatum::OperandInformation::isSufficient) == 1, + "unexpected value for size of FmqResultDatum::OperandInformation::isSufficient"); + static_assert(offsetof(FmqResultDatum::OperandInformation, numberOfDimensions) == 4, + "unexpected value for offset of " + "FmqResultDatum::OperandInformation::numberOfDimensions"); + static_assert(sizeof(FmqResultDatum::OperandInformation::numberOfDimensions) == 4, + "unexpected value for size of " + "FmqResultDatum::OperandInformation::numberOfDimensions"); + static_assert(sizeof(FmqResultDatum::OperandInformation) == 8, + "unexpected value for size of " + "FmqResultDatum::OperandInformation"); + + constexpr size_t paddingOffset = + offsetof(FmqResultDatum::OperandInformation, isSufficient) + + sizeof(FmqResultDatum::OperandInformation::isSufficient); + constexpr size_t paddingSize = + offsetof(FmqResultDatum::OperandInformation, numberOfDimensions) - paddingOffset; + + FmqResultDatum::OperandInformation initialized{}; + std::memset(&initialized, 0, sizeof(initialized)); + + const char* initializedPaddingStart = + reinterpret_cast(&initialized) + paddingOffset; + const char* datumPaddingStart = + reinterpret_cast(&datum.operandInformation()) + paddingOffset; + + return std::memcmp(datumPaddingStart, initializedPaddingStart, paddingSize) == 0; + } + + // there are no other padding initialization checks required, so return true + // for any sum-type that isn't FmqResultDatum::OperandInformation + return true; +} + +static void validateBurstSanitized(const sp& preparedModel, + const std::vector& requests) { + // create burst + std::unique_ptr sender; + std::unique_ptr receiver; + sp callback = new ExecutionBurstCallback(); + sp context; + ASSERT_NO_FATAL_FAILURE(createBurst(preparedModel, callback, &sender, &receiver, &context)); + ASSERT_NE(nullptr, sender.get()); + ASSERT_NE(nullptr, receiver.get()); + ASSERT_NE(nullptr, context.get()); + + // validate each request + for (const Request& request : requests) { + // load memory into callback slots + std::vector keys; + keys.reserve(request.pools.size()); + std::transform(request.pools.begin(), request.pools.end(), std::back_inserter(keys), + [](const auto& pool) { return reinterpret_cast(&pool); }); + const std::vector slots = callback->getSlots(request.pools, keys); + + // send valid request + ASSERT_TRUE(sender->send(request, MeasureTiming::YES, slots)); + + // receive valid result + auto serialized = receiver->getPacketBlocking(); + ASSERT_TRUE(serialized.has_value()); + + // sanitize result + ASSERT_TRUE(std::all_of(serialized->begin(), serialized->end(), isSanitized)) + << "The result serialized data is not properly sanitized"; + } +} + ///////////////////////////// ENTRY POINT ////////////////////////////////// void ValidationTest::validateBurst(const sp& preparedModel, const std::vector& requests) { ASSERT_NO_FATAL_FAILURE(validateBurstSerialization(preparedModel, requests)); ASSERT_NO_FATAL_FAILURE(validateBurstFmqLength(preparedModel, requests)); + ASSERT_NO_FATAL_FAILURE(validateBurstSanitized(preparedModel, requests)); } } // namespace functional