/* * Copyright (C) 2016, 2018-2020 ARM Limited. All rights reserved. * * Copyright (C) 2008 The Android Open Source Project * * 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 "mali_gralloc_reference.h" #include #include #include #include #include "allocator/mali_gralloc_ion.h" #include "allocator/mali_gralloc_shared_memory.h" #include "mali_gralloc_buffer.h" #include "mali_gralloc_bufferallocation.h" #include "mali_gralloc_usages.h" class BufferManager { private: // This struct for now is just for validation and reference counting. When we // are sure that private_handle_t::bases is not being used outside gralloc, this // should become the only place where address mapping is maintained and can be // queried from. struct MappedData { void *bases[MAX_BUFFER_FDS] = {}; size_t alloc_sizes[MAX_BUFFER_FDS] = {}; uint64_t ref_count = 0; }; BufferManager() = default; std::mutex lock; std::map> buffer_map GUARDED_BY(lock); static bool should_map_dmabuf(buffer_handle_t handle) { private_handle_t *hnd = (private_handle_t *)handle; // TODO(b/187145254): CPU_READ/WRITE buffer is not being properly locked from // MFC. This is a WA for the time being. constexpr auto cpu_access_usage = (GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_RARELY | GRALLOC_USAGE_SW_READ_RARELY); return hnd->get_usage() & cpu_access_usage; } int map_locked(buffer_handle_t handle) REQUIRES(lock) { private_handle_t *hnd = (private_handle_t *)handle; auto it = buffer_map.find(hnd); if (it == buffer_map.end()) { MALI_GRALLOC_LOGE("BUG: Map called without importing buffer"); return -EINVAL; } auto &data = *(it->second.get()); if (data.ref_count == 0) { MALI_GRALLOC_LOGE("BUG: Found an imported buffer with ref count 0, expect errors"); } if (data.bases[0] != nullptr) { return 0; } for (auto i = 0; i < MAX_BUFFER_FDS; i++) { auto size = get_buffer_size(hnd->fds[i]); auto size_padding = size - (off_t)hnd->alloc_sizes[i]; if ((size != -1) && ((size_padding < 0) || (size_padding > PAGE_SIZE))){ MALI_GRALLOC_LOGE("Found an imported buffer with out-of-bounds size %" PRIu64 "", hnd->alloc_sizes[i]); return -EINVAL; } } int error = mali_gralloc_ion_map(hnd); if (error != 0) { return error; } for (auto i = 0; i < MAX_BUFFER_FDS; i++) { data.bases[i] = reinterpret_cast(hnd->bases[i]); data.alloc_sizes[i] = hnd->alloc_sizes[i]; } return 0; } int validate_locked(buffer_handle_t handle) REQUIRES(lock) { if (private_handle_t::validate(handle) < 0) { MALI_GRALLOC_LOGE("Reference invalid buffer %p, returning error", handle); return -EINVAL; } const auto *hnd = (private_handle_t *)handle; auto it = buffer_map.find(hnd); if (it == buffer_map.end()) { MALI_GRALLOC_LOGE("Reference unimported buffer %p, returning error", handle); return -EINVAL; } auto &data = *(it->second.get()); if (data.bases[0] != nullptr) { for (auto i = 0; i < MAX_BUFFER_FDS; i++) { if (data.bases[i] != reinterpret_cast(hnd->bases[i]) || data.alloc_sizes[i] != hnd->alloc_sizes[i]) { MALI_GRALLOC_LOGE( "Validation failed: Buffer attributes inconsistent with mapper"); return -EINVAL; } } } else { for (auto i = 0; i < MAX_BUFFER_FDS; i++) { if (hnd->bases[i] != 0 || data.bases[i] != nullptr) { MALI_GRALLOC_LOGE("Validation failed: Expected nullptr for unmapped buffer"); return -EINVAL; } } } return 0; } off_t get_buffer_size(unsigned int fd) { off_t current = lseek(fd, 0, SEEK_CUR); off_t size = lseek(fd, 0, SEEK_END); lseek(fd, current, SEEK_SET); return size; } public: static BufferManager &getInstance() { static BufferManager instance; return instance; } int retain(buffer_handle_t handle) EXCLUDES(lock) { if (private_handle_t::validate(handle) < 0) { MALI_GRALLOC_LOGE("Registering/Retaining invalid buffer %p, returning error", handle); return -EINVAL; } std::lock_guard _l(lock); private_handle_t *hnd = (private_handle_t *)handle; auto it = buffer_map.find(hnd); if (it == buffer_map.end()) { bool success = false; auto _data = std::make_unique(); std::tie(it, success) = buffer_map.insert({hnd, std::move(_data)}); if (!success) { MALI_GRALLOC_LOGE("Failed to create buffer data mapping"); return -EINVAL; } for (int i = 0; i < MAX_BUFFER_FDS; i++) { hnd->bases[i] = 0; } } else if (it->second->ref_count == 0) { MALI_GRALLOC_LOGE("BUG: Import counter of an imported buffer is 0, expect errors"); } auto &data = *(it->second.get()); data.ref_count++; if (!should_map_dmabuf(handle)) return 0; return map_locked(handle); } int map(buffer_handle_t handle) EXCLUDES(lock) { std::lock_guard _l(lock); auto error = validate_locked(handle); if (error != 0) { return error; } return map_locked(handle); } int release(buffer_handle_t handle) EXCLUDES(lock) { std::lock_guard _l(lock); // Always call locked variant of validate from this function. On calling // the other validate variant, an attacker might launch a timing attack // where they would try to time their attack between the return of // validate and before taking the lock in this function again. auto error = validate_locked(handle); if (error != 0) { return error; } private_handle_t *hnd = (private_handle_t *)handle; auto it = buffer_map.find(hnd); if (it == buffer_map.end()) { MALI_GRALLOC_LOGE("Trying to release a non-imported buffer"); return -EINVAL; } auto &data = *(it->second.get()); if (data.ref_count == 0) { MALI_GRALLOC_LOGE("BUG: Reference held for buffer whose counter is 0"); return -EINVAL; } data.ref_count--; if (data.ref_count == 0) { if (data.bases[0] != nullptr) { mali_gralloc_ion_unmap(hnd); } /* TODO: Make this unmapping of shared meta fd into a function? */ if (hnd->attr_base) { munmap(hnd->attr_base, hnd->attr_size); hnd->attr_base = nullptr; } buffer_map.erase(it); } return 0; } int validate(buffer_handle_t handle) EXCLUDES(lock) { std::lock_guard _l(lock); return validate_locked(handle); } }; int mali_gralloc_reference_retain(buffer_handle_t handle) { return BufferManager::getInstance().retain(handle); } int mali_gralloc_reference_map(buffer_handle_t handle) { return BufferManager::getInstance().map(handle); } int mali_gralloc_reference_release(buffer_handle_t handle) { return BufferManager::getInstance().release(handle); } int mali_gralloc_reference_validate(buffer_handle_t handle) { return BufferManager::getInstance().validate(handle); }