Memory Domain Runtime: Device memory as execution I/O.

- Add validation checks in NNAPI runtime when using driver-allocated
  memory as execution I/O.
- Add validation tests.

Additionally, change the result code of using non-BLOB mode AHWB with
ANNModel_setOperandValueFromMemory to BAD_DATA.

Bug: 141353602
Bug: 141363565
Test: NNT_static
Change-Id: I6f253a0a90a0c1b2baa186034482215435f3c831
Merged-In: I6f253a0a90a0c1b2baa186034482215435f3c831
(cherry picked from commit 3c0d4fc46c1d66ae5489885ed44beb43c07130ff)
diff --git a/runtime/Memory.cpp b/runtime/Memory.cpp
index 058a31a..561df07 100644
--- a/runtime/Memory.cpp
+++ b/runtime/Memory.cpp
@@ -37,6 +37,124 @@
 
 using namespace hal;
 
+namespace {
+
+// The validator for a client-managed single-dimensional memory pool with a known size.
+// The memory may be used for request inputs, request outputs, or model constants.
+class SizedMemoryValidator : public MemoryValidatorBase {
+   public:
+    SizedMemoryValidator(uint32_t size) : kSize(size) {}
+
+    bool validate(const CompilationBuilder*, IOType, uint32_t, const ANeuralNetworksOperandType*,
+                  uint32_t offset, uint32_t length) const override {
+        NN_RET_CHECK(offset + length <= kSize) << "request size larger than the memory size.";
+        NN_RET_CHECK(offset != 0 || length != 0) << "memory size cannot be implied.";
+        return true;
+    }
+
+   private:
+    const uint32_t kSize;
+};
+
+// The validator for an AHardwareBuffer with Non-BLOB format.
+// We require the memory only used for request inputs or request outputs,
+// with both offset and length set to zero.
+class AHardwareBufferNonBlobValidator : public MemoryValidatorBase {
+   public:
+    AHardwareBufferNonBlobValidator() = default;
+
+    bool validate(const CompilationBuilder* compilation, IOType, uint32_t,
+                  const ANeuralNetworksOperandType*, uint32_t offset,
+                  uint32_t length) const override {
+        NN_RET_CHECK(compilation != nullptr)
+                << "cannot use Non-BLOB AHardwareBuffer as model constant";
+        NN_RET_CHECK(offset == 0 && length == 0)
+                << "non-zero offset (" << offset << ") and/or length (" << length
+                << ") for Non-BLOB format AHardwareBuffer.";
+        return true;
+    }
+};
+
+// The validator for a memory created from ANNMemory_createFromDesc.
+// We require the memory only used as one of the pre-specified roles,
+// with both offset and length set to zero.
+class DeviceMemoryValidator : public MemoryValidatorBase {
+   public:
+    DeviceMemoryValidator(std::set<CompilationRole> roles, hal::OperandType type,
+                          std::vector<uint32_t> dimensions)
+        : kCompilationRoles(std::move(roles)),
+          mDataType(type),
+          kInitialDimensions(std::move(dimensions)),
+          mUpdatedDimensions(kInitialDimensions) {}
+
+    bool validate(const CompilationBuilder* compilation, IOType ioType, uint32_t index,
+                  const ANeuralNetworksOperandType* type, uint32_t offset,
+                  uint32_t length) const override {
+        NN_RET_CHECK(kCompilationRoles.count({compilation, ioType, index}) > 0)
+                << "invalid compilation role.";
+        NN_RET_CHECK(offset == 0 && length == 0)
+                << "non-zero offset and/or length for driver-allocated memory.";
+        if (type) {
+            const bool isTensor = TypeManager::get()->isTensorType(mDataType);
+            NN_RET_CHECK(isTensor || type->dimensionCount == 0)
+                    << "invalid dimensions for scalar memory.";
+            std::vector<uint32_t> dimensions(type->dimensions,
+                                             type->dimensions + type->dimensionCount);
+            // We only check against kInitialDimensions here.
+            // For input memories, mUpdatedDimensions will be checked in validateInputDimensions
+            // at the beginning of a computation.
+            const auto combined = combineDimensions(dimensions, kInitialDimensions);
+            NN_RET_CHECK(combined.has_value())
+                    << "incompatible dimensions between request and memory. (request: "
+                    << toString(dimensions) << ", memory: " << toString(kInitialDimensions) << ")";
+        }
+        return true;
+    }
+
+    bool validateInputDimensions(const std::vector<uint32_t>& dimensions) const override {
+        NN_RET_CHECK(mInitialized) << "using an uninitialized memory as input";
+        NN_RET_CHECK(dimensions == mUpdatedDimensions)
+                << "incompatible input dimensions between request and memory. (request: "
+                << toString(dimensions) << ", memory: " << toString(mUpdatedDimensions) << ")";
+        return true;
+    }
+
+    bool updateDimensions(const std::vector<uint32_t>& dimensions) override {
+        NN_RET_CHECK(TypeManager::get()->isTensorType(mDataType) || dimensions.empty());
+        auto combined = combineDimensions(dimensions, kInitialDimensions);
+        NN_RET_CHECK(combined.has_value());
+        mUpdatedDimensions = std::move(combined.value());
+        return true;
+    }
+
+    void setInitialized(bool initialized) override { mInitialized = initialized; }
+
+   private:
+    const std::set<CompilationRole> kCompilationRoles;
+    OperandType mDataType;
+
+    // The dimensions of the memory when the memory object is created.
+    // May have unknown dimensions or rank.
+    const std::vector<uint32_t> kInitialDimensions;
+
+    // The updated dimensions after a successful execution or memory copying.
+    std::vector<uint32_t> mUpdatedDimensions;
+
+    bool mInitialized = false;
+};
+
+}  // namespace
+
+Memory::Memory(hal::hidl_memory memory)
+    : kHidlMemory(std::move(memory)),
+      mValidator(std::make_unique<SizedMemoryValidator>(kHidlMemory.size())) {}
+
+Memory::Memory(hal::hidl_memory memory, std::unique_ptr<MemoryValidatorBase> validator)
+    : kHidlMemory(std::move(memory)), mValidator(std::move(validator)) {}
+
+Memory::Memory(sp<hal::IBuffer> buffer, int32_t token)
+    : kBuffer(std::move(buffer)), kToken(token) {}
+
 Memory::~Memory() {
     for (const auto [ptr, weakBurst] : mUsedBy) {
         if (const std::shared_ptr<ExecutionBurstController> burst = weakBurst.lock()) {
@@ -55,14 +173,6 @@
     return pool;
 }
 
-bool Memory::validateSize(uint32_t offset, uint32_t length) const {
-    if (offset + length > kHidlMemory.size()) {
-        LOG(ERROR) << "Request size larger than the memory size.";
-        return false;
-    }
-    return true;
-}
-
 intptr_t Memory::getKey() const {
     return reinterpret_cast<intptr_t>(this);
 }
@@ -261,6 +371,13 @@
         VLOG(MEMORY) << "MemoryBuilder::allocate -- fallback to ashmem.";
         std::tie(n, memory) = MemoryAshmem::create(size);
     }
+
+    if (n == ANEURALNETWORKS_NO_ERROR) {
+        CHECK(memory != nullptr);
+        auto validator =
+                std::make_unique<DeviceMemoryValidator>(mRoles, mOperand->type, mDesc.dimensions);
+        memory->setValidator(std::move(validator));
+    }
     return {n, std::move(memory)};
 }
 
@@ -331,31 +448,20 @@
     AHardwareBuffer_describe(&ahwb, &bufferDesc);
     const native_handle_t* handle = AHardwareBuffer_getNativeHandle(&ahwb);
     hidl_memory hidlMemory;
+    std::unique_ptr<MemoryAHWB> memory;
+    std::unique_ptr<MemoryValidatorBase> validator;
     if (bufferDesc.format == AHARDWAREBUFFER_FORMAT_BLOB) {
         hidlMemory = hidl_memory("hardware_buffer_blob", handle, bufferDesc.width);
+        validator = std::make_unique<SizedMemoryValidator>(bufferDesc.width);
     } else {
         // memory size is not used.
         hidlMemory = hidl_memory("hardware_buffer", handle, 0);
+        validator = std::make_unique<AHardwareBufferNonBlobValidator>();
     }
-
-    std::unique_ptr<MemoryAHWB> memory =
-            std::make_unique<MemoryAHWB>(bufferDesc, std::move(hidlMemory));
+    memory = std::make_unique<MemoryAHWB>(std::move(hidlMemory), std::move(validator));
     return {ANEURALNETWORKS_NO_ERROR, std::move(memory)};
 };
 
-bool MemoryAHWB::validateSize(uint32_t offset, uint32_t length) const {
-    // validateSize should only be called on BLOB mode buffer.
-    if (!kBlobMode) {
-        LOG(ERROR) << "Invalid AHARDWAREBUFFER_FORMAT, must be AHARDWAREBUFFER_FORMAT_BLOB.";
-        return false;
-    }
-    // Use normal validation.
-    return Memory::validateSize(offset, length);
-}
-
-MemoryAHWB::MemoryAHWB(const AHardwareBuffer_Desc& desc, hidl_memory memory)
-    : Memory(std::move(memory)), kBlobMode(desc.format == AHARDWAREBUFFER_FORMAT_BLOB) {}
-
 std::pair<int, std::unique_ptr<MemoryFromDevice>> MemoryFromDevice::create(sp<hal::IBuffer> buffer,
                                                                            int32_t token) {
     if (buffer == nullptr) {