diff options
Diffstat (limited to 'src/libcamera/ipa_module.cpp')
-rw-r--r-- | src/libcamera/ipa_module.cpp | 296 |
1 files changed, 145 insertions, 151 deletions
diff --git a/src/libcamera/ipa_module.cpp b/src/libcamera/ipa_module.cpp index a01d0757..9ca74be6 100644 --- a/src/libcamera/ipa_module.cpp +++ b/src/libcamera/ipa_module.cpp @@ -2,28 +2,29 @@ /* * Copyright (C) 2019, Google Inc. * - * ipa_module.cpp - Image Processing Algorithm module + * Image Processing Algorithm module */ -#include "ipa_module.h" +#include "libcamera/internal/ipa_module.h" #include <algorithm> -#include <array> +#include <ctype.h> #include <dlfcn.h> #include <elf.h> #include <errno.h> #include <fcntl.h> #include <link.h> #include <string.h> -#include <sys/mman.h> #include <sys/stat.h> #include <sys/types.h> -#include <tuple> #include <unistd.h> -#include "log.h" -#include "pipeline_handler.h" -#include "utils.h" +#include <libcamera/base/file.h> +#include <libcamera/base/log.h> +#include <libcamera/base/span.h> +#include <libcamera/base/utils.h> + +#include "libcamera/internal/pipeline_handler.h" /** * \file ipa_module.h @@ -42,27 +43,27 @@ LOG_DEFINE_CATEGORY(IPAModule) namespace { template<typename T> -typename std::remove_extent_t<T> *elfPointer(void *map, off_t offset, - size_t fileSize, size_t objSize) +typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf, + off_t offset, size_t objSize) { size_t size = offset + objSize; - if (size > fileSize || size < objSize) + if (size > elf.size() || size < objSize) return nullptr; - return reinterpret_cast<typename std::remove_extent_t<T> *> - (static_cast<char *>(map) + offset); + return reinterpret_cast<typename std::remove_extent_t<T> *>( + reinterpret_cast<const char *>(elf.data()) + offset); } template<typename T> -typename std::remove_extent_t<T> *elfPointer(void *map, off_t offset, - size_t fileSize) +typename std::remove_extent_t<T> *elfPointer(Span<const uint8_t> elf, + off_t offset) { - return elfPointer<T>(map, offset, fileSize, sizeof(T)); + return elfPointer<T>(elf, offset, sizeof(T)); } -int elfVerifyIdent(void *map, size_t soSize) +int elfVerifyIdent(Span<const uint8_t> elf) { - char *e_ident = elfPointer<char[EI_NIDENT]>(map, 0, soSize); + const char *e_ident = elfPointer<const char[EI_NIDENT]>(elf, 0); if (!e_ident) return -ENOEXEC; @@ -86,40 +87,47 @@ int elfVerifyIdent(void *map, size_t soSize) return 0; } +const ElfW(Shdr) *elfSection(Span<const uint8_t> elf, const ElfW(Ehdr) *eHdr, + ElfW(Half) idx) +{ + if (idx >= eHdr->e_shnum) + return nullptr; + + off_t offset = eHdr->e_shoff + idx * + static_cast<uint32_t>(eHdr->e_shentsize); + return elfPointer<const ElfW(Shdr)>(elf, offset); +} + /** * \brief Retrieve address and size of a symbol from an mmap'ed ELF file - * \param[in] map Address of mmap'ed ELF file - * \param[in] soSize Size of mmap'ed ELF file (in bytes) + * \param[in] elf Address and size of mmap'ed ELF file * \param[in] symbol Symbol name * - * \return zero or error code, address or nullptr, size of symbol or zero, - * respectively + * \return The memory region storing the symbol on success, or an empty span + * otherwise */ -std::tuple<void *, size_t> -elfLoadSymbol(void *map, size_t soSize, const char *symbol) +Span<const uint8_t> elfLoadSymbol(Span<const uint8_t> elf, const char *symbol) { - ElfW(Ehdr) *eHdr = elfPointer<ElfW(Ehdr)>(map, 0, soSize); + const ElfW(Ehdr) *eHdr = elfPointer<const ElfW(Ehdr)>(elf, 0); if (!eHdr) - return std::make_tuple(nullptr, 0); + return {}; - off_t offset = eHdr->e_shoff + eHdr->e_shentsize * eHdr->e_shstrndx; - ElfW(Shdr) *sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize); + const ElfW(Shdr) *sHdr = elfSection(elf, eHdr, eHdr->e_shstrndx); if (!sHdr) - return std::make_tuple(nullptr, 0); + return {}; off_t shnameoff = sHdr->sh_offset; /* Locate .dynsym section header. */ - ElfW(Shdr) *dynsym = nullptr; + const ElfW(Shdr) *dynsym = nullptr; for (unsigned int i = 0; i < eHdr->e_shnum; i++) { - offset = eHdr->e_shoff + eHdr->e_shentsize * i; - sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize); + sHdr = elfSection(elf, eHdr, i); if (!sHdr) - return std::make_tuple(nullptr, 0); + return {}; - offset = shnameoff + sHdr->sh_name; - char *name = elfPointer<char[8]>(map, offset, soSize); + off_t offset = shnameoff + sHdr->sh_name; + const char *name = elfPointer<const char[8]>(elf, offset); if (!name) - return std::make_tuple(nullptr, 0); + return {}; if (sHdr->sh_type == SHT_DYNSYM && !strcmp(name, ".dynsym")) { dynsym = sHdr; @@ -129,29 +137,28 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol) if (dynsym == nullptr) { LOG(IPAModule, Error) << "ELF has no .dynsym section"; - return std::make_tuple(nullptr, 0); + return {}; } - offset = eHdr->e_shoff + eHdr->e_shentsize * dynsym->sh_link; - sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize); + sHdr = elfSection(elf, eHdr, dynsym->sh_link); if (!sHdr) - return std::make_tuple(nullptr, 0); + return {}; off_t dynsym_nameoff = sHdr->sh_offset; /* Locate symbol in the .dynsym section. */ - ElfW(Sym) *targetSymbol = nullptr; + const ElfW(Sym) *targetSymbol = nullptr; unsigned int dynsym_num = dynsym->sh_size / dynsym->sh_entsize; for (unsigned int i = 0; i < dynsym_num; i++) { - offset = dynsym->sh_offset + dynsym->sh_entsize * i; - ElfW(Sym) *sym = elfPointer<ElfW(Sym)>(map, offset, soSize); + off_t offset = dynsym->sh_offset + dynsym->sh_entsize * i; + const ElfW(Sym) *sym = elfPointer<const ElfW(Sym)>(elf, offset); if (!sym) - return std::make_tuple(nullptr, 0); + return {}; offset = dynsym_nameoff + sym->st_name; - char *name = elfPointer<char>(map, offset, soSize, - strlen(symbol) + 1); + const char *name = elfPointer<const char>(elf, offset, + strlen(symbol) + 1); if (!name) - return std::make_tuple(nullptr, 0); + return {}; if (!strcmp(name, symbol) && sym->st_info & STB_GLOBAL) { @@ -162,22 +169,20 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol) if (targetSymbol == nullptr) { LOG(IPAModule, Error) << "Symbol " << symbol << " not found"; - return std::make_tuple(nullptr, 0); + return {}; } /* Locate and return data of symbol. */ - if (targetSymbol->st_shndx >= eHdr->e_shnum) - return std::make_tuple(nullptr, 0); - offset = eHdr->e_shoff + targetSymbol->st_shndx * eHdr->e_shentsize; - sHdr = elfPointer<ElfW(Shdr)>(map, offset, soSize); + sHdr = elfSection(elf, eHdr, targetSymbol->st_shndx); if (!sHdr) - return std::make_tuple(nullptr, 0); - offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr); - char *data = elfPointer<char>(map, offset, soSize, targetSymbol->st_size); + return {}; + off_t offset = sHdr->sh_offset + (targetSymbol->st_value - sHdr->sh_addr); + const uint8_t *data = elfPointer<const uint8_t>(elf, offset, + targetSymbol->st_size); if (!data) - return std::make_tuple(nullptr, 0); + return {}; - return std::make_tuple(data, targetSymbol->st_size); + return { data, targetSymbol->st_size }; } } /* namespace */ @@ -218,26 +223,10 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol) * \var IPAModuleInfo::name * \brief The name of the IPA module * - * \var IPAModuleInfo::license - * \brief License of the IPA module - * - * This license is used to determine whether to force isolation of the IPA in - * a separate process. If the license is "Proprietary", then the IPA will - * be isolated. If the license is open-source, then the IPA will be allowed to - * run without isolation if the user enables it. The license should be an - * SPDX license string. The following licenses are currently available to - * allow the IPA to run unisolated: - * - * - GPL-2.0-only - * - GPL-2.0-or-later - * - GPL-3.0-only - * - GPL-3.0-or-later - * - LGPL-2.1-only - * - LGPL-2.1-or-later - * - LGPL-3.0-only - * - LGPL-3.0-or-later - * - * Any other license will cause the IPA to be run isolated. + * The name may be used to build file system paths to IPA-specific resources. + * It shall only contain printable characters, and may not contain '*', '?' or + * '\'. For IPA modules included in libcamera, it shall match the directory of + * the IPA module in the source tree. * * \todo Allow user to choose to isolate open source IPAs */ @@ -262,7 +251,7 @@ elfLoadSymbol(void *map, size_t soSize, const char *symbol) * The IPA module shared object file must be of the same endianness and * bitness as libcamera. * - * The caller shall call the isValid() method after constructing an + * The caller shall call the isValid() function after constructing an * IPAModule instance to verify the validity of the IPAModule. */ IPAModule::IPAModule(const std::string &libPath) @@ -283,55 +272,67 @@ IPAModule::~IPAModule() int IPAModule::loadIPAModuleInfo() { - int fd = open(libPath_.c_str(), O_RDONLY); - if (fd < 0) { - int ret = -errno; + File file{ libPath_ }; + if (!file.open(File::OpenModeFlag::ReadOnly)) { LOG(IPAModule, Error) << "Failed to open IPA library: " - << strerror(-ret); - return ret; + << strerror(-file.error()); + return file.error(); } - void *data = nullptr; - size_t dataSize; - void *map; - size_t soSize; - struct stat st; - int ret = fstat(fd, &st); - if (ret < 0) - goto close; - soSize = st.st_size; - map = mmap(NULL, soSize, PROT_READ, MAP_PRIVATE, fd, 0); - if (map == MAP_FAILED) { - ret = -errno; - goto close; + Span<const uint8_t> data = file.map(); + int ret = elfVerifyIdent(data); + if (ret) { + LOG(IPAModule, Error) << "IPA module is not an ELF file"; + return ret; } - ret = elfVerifyIdent(map, soSize); - if (ret) - goto unmap; - - std::tie(data, dataSize) = elfLoadSymbol(map, soSize, "ipaModuleInfo"); - - if (data && dataSize == sizeof(info_)) - memcpy(&info_, data, dataSize); + Span<const uint8_t> info = elfLoadSymbol(data, "ipaModuleInfo"); + if (info.size() < sizeof(info_)) { + LOG(IPAModule, Error) << "IPA module has no valid info"; + return -EINVAL; + } - if (!data) - goto unmap; + memcpy(&info_, info.data(), sizeof(info_)); if (info_.moduleAPIVersion != IPA_MODULE_API_VERSION) { LOG(IPAModule, Error) << "IPA module API version mismatch"; - ret = -EINVAL; + return -EINVAL; } -unmap: - munmap(map, soSize); -close: - if (ret || !data) + /* + * Validate the IPA module name. + * + * \todo Consider module naming restrictions to avoid escaping from a + * base directory. Forbidding ".." may be enough, but this may be best + * implemented in a different layer. + */ + std::string ipaName = info_.name; + auto iter = std::find_if_not(ipaName.begin(), ipaName.end(), + [](unsigned char c) -> bool { + return isprint(c) && c != '?' && + c != '*' && c != '\\'; + }); + if (iter != ipaName.end()) { LOG(IPAModule, Error) - << "Error loading IPA module info for " << libPath_; + << "Invalid IPA module name '" << ipaName << "'"; + return -EINVAL; + } - close(fd); - return ret; + /* Load the signature. Failures are not fatal. */ + File sign{ libPath_ + ".sign" }; + if (!sign.open(File::OpenModeFlag::ReadOnly)) { + LOG(IPAModule, Debug) + << "IPA module " << libPath_ << " is not signed"; + return 0; + } + + data = sign.map(0, -1, File::MapFlag::Private); + signature_.resize(data.size()); + memcpy(signature_.data(), data.data(), data.size()); + + LOG(IPAModule, Debug) << "IPA module " << libPath_ << " is signed"; + + return 0; } /** @@ -363,6 +364,21 @@ const struct IPAModuleInfo &IPAModule::info() const } /** + * \brief Retrieve the IPA module signature + * + * The IPA module signature is stored alongside the IPA module in a file with a + * '.sign' suffix, and is loaded when the IPAModule instance is created. This + * function returns the signature without verifying it. If the signature is + * missing, the returned vector will be empty. + * + * \return The IPA module signature + */ +const std::vector<uint8_t> IPAModule::signature() const +{ + return signature_; +} + +/** * \brief Retrieve the IPA module path * * The IPA module path is the file name and path of the IPA module shared @@ -378,13 +394,13 @@ const std::string &IPAModule::path() const /** * \brief Load the IPA implementation factory from the shared object * - * The IPA module shared object implements an ipa_context object to be used - * by pipeline handlers. This method loads the factory function from the - * shared object. Later, createContext() can be called to instantiate the - * ipa_context. + * The IPA module shared object implements an IPAInterface object to be used + * by pipeline handlers. This function loads the factory function from the + * shared object. Later, createInterface() can be called to instantiate the + * IPAInterface. * - * This method only needs to be called successfully once, after which - * createContext() can be called as many times as ipa_context instances are + * This function only needs to be called successfully once, after which + * createInterface() can be called as many times as IPAInterface instances are * needed. * * Calling this function on an invalid module (as returned by isValid()) is @@ -426,20 +442,18 @@ bool IPAModule::load() } /** - * \brief Instantiate an IPA context + * \brief Instantiate an IPA interface * - * After loading the IPA module with load(), this method creates an instance of - * the IPA module context. Ownership of the context is passed to the caller, and - * the context shall be destroyed by calling the \ref ipa_context_ops::destroy - * "ipa_context::ops::destroy()" function. + * After loading the IPA module with load(), this function creates an instance + * of the IPA module interface. * * Calling this function on a module that has not yet been loaded, or an * invalid module (as returned by load() and isValid(), respectively) is * an error. * - * \return The IPA context on success, or nullptr on error + * \return The IPA interface on success, or nullptr on error */ -struct ipa_context *IPAModule::createContext() +IPAInterface *IPAModule::createInterface() { if (!valid_ || !loaded_) return nullptr; @@ -448,12 +462,12 @@ struct ipa_context *IPAModule::createContext() } /** - * \brief Verify if the IPA module maches a given pipeline handler + * \brief Verify if the IPA module matches a given pipeline handler * \param[in] pipe Pipeline handler to match with * \param[in] minVersion Minimum acceptable version of IPA module * \param[in] maxVersion Maximum acceptable version of IPA module * - * This method checks if this IPA module matches the \a pipe pipeline handler, + * This function checks if this IPA module matches the \a pipe pipeline handler, * and the input version range. * * \return True if the pipeline handler matches the IPA module, or false otherwise @@ -466,29 +480,9 @@ bool IPAModule::match(PipelineHandler *pipe, !strcmp(info_.pipelineName, pipe->name()); } -/** - * \brief Verify if the IPA module is open source - * - * \sa IPAModuleInfo::license - */ -bool IPAModule::isOpenSource() const +std::string IPAModule::logPrefix() const { - static const char *osLicenses[] = { - "GPL-2.0-only", - "GPL-2.0-or-later", - "GPL-3.0-only", - "GPL-3.0-or-later", - "LGPL-2.1-only", - "LGPL-2.1-or-later", - "LGPL-3.0-only", - "LGPL-3.0-or-later", - }; - - for (unsigned int i = 0; i < ARRAY_SIZE(osLicenses); i++) - if (!strcmp(osLicenses[i], info_.license)) - return true; - - return false; + return utils::basename(libPath_.c_str()); } } /* namespace libcamera */ |