| // Copyright 2020 The Fuchsia Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| // Device independent functions to validate partition data and disk images. |
| // Tools to validate |
| |
| #include "validation.h" |
| |
| #include <lib/cksum.h> |
| #include <lib/stdcompat/span.h> |
| #include <zircon/errors.h> |
| #include <zircon/status.h> |
| |
| #include <fbl/algorithm.h> |
| #include <fbl/alloc_checker.h> |
| |
| #include "device-partitioner.h" |
| #include "pave-logging.h" |
| |
| namespace paver { |
| |
| namespace { |
| |
| // Magic header of ChromeOS kernel verification block. |
| constexpr std::string_view kChromeOsMagicHeader = "CHROMEOS"; |
| |
| // Determine if the CRC of the given zbi_header_t is valid. |
| // |
| // We require that the "hdr" has "hdr->length" valid bytes after it. |
| bool ZbiHeaderCrcValid(const zbi_header_t* hdr) { |
| // If we don't have the CRC32 flag set, ensure no crc32 value is given. |
| if ((hdr->flags & ZBI_FLAG_CRC32) == 0) { |
| return (hdr->crc32 == ZBI_ITEM_NO_CRC32); |
| } |
| |
| // Otherwise, calculate the CRC. |
| return hdr->crc32 == crc32(0, reinterpret_cast<const uint8_t*>(hdr + 1), hdr->length); |
| } |
| |
| } // namespace |
| |
| bool ExtractZbiPayload(cpp20::span<const uint8_t> data, const zbi_header_t** header, |
| cpp20::span<const uint8_t>* payload) { |
| // Validate data header. |
| if (data.size() < sizeof(zbi_header_t)) { |
| ERROR("Data too short: expected at least %ld byte(s), got %ld byte(s).\n", sizeof(zbi_header_t), |
| data.size()); |
| return false; |
| } |
| |
| // Validate the header. |
| const auto zbi_header = reinterpret_cast<const zbi_header_t*>(data.data()); |
| if (zbi_header->magic != ZBI_ITEM_MAGIC) { |
| ERROR("ZBI header has incorrect magic value.\n"); |
| return false; |
| } |
| if ((zbi_header->flags & ZBI_FLAG_VERSION) != ZBI_FLAG_VERSION) { |
| ERROR("ZBI header has invalid version.\n"); |
| return false; |
| } |
| |
| // Ensure the data length is valid. We are okay with additional bytes |
| // at the end of the data, but not having too few bytes available. |
| if (zbi_header->length > data.size() - sizeof(zbi_header_t)) { |
| ERROR("Header length length of %u byte(s) exceeds data available of %ld byte(s).\n", |
| zbi_header->length, data.size() - sizeof(zbi_header_t)); |
| return false; |
| } |
| |
| // Verify CRC. |
| if (!ZbiHeaderCrcValid(zbi_header)) { |
| ERROR("ZBI payload CRC invalid.\n"); |
| return false; |
| } |
| |
| // All good. |
| *header = zbi_header; |
| *payload = data.subspan(sizeof(zbi_header_t), zbi_header->length); |
| return true; |
| } |
| |
| bool IsValidKernelZbi(Arch arch, cpp20::span<const uint8_t> data) { |
| // Get container header. |
| const zbi_header_t* container_header; |
| cpp20::span<const uint8_t> container_data; |
| if (!ExtractZbiPayload(data, &container_header, &container_data)) { |
| return false; |
| } |
| |
| // Ensure it is of the correct type. |
| if (container_header->type != ZBI_TYPE_CONTAINER) { |
| ERROR("ZBI container not a container type, or has invalid magic value.\n"); |
| return false; |
| } |
| if (container_header->extra != ZBI_CONTAINER_MAGIC) { |
| ERROR("ZBI container has invalid magic value.\n"); |
| return false; |
| } |
| |
| // Extract kernel. |
| const zbi_header_t* kernel_header; |
| cpp20::span<const uint8_t> kernel_data; |
| if (!ExtractZbiPayload(container_data, &kernel_header, &kernel_data)) { |
| return false; |
| } |
| |
| // Ensure it is of the correct type. |
| const uint32_t expected_kernel_type = |
| (arch == Arch::kX64) ? ZBI_TYPE_KERNEL_X64 : ZBI_TYPE_KERNEL_ARM64; |
| if (kernel_header->type != expected_kernel_type) { |
| ERROR("ZBI kernel payload has incorrect type or architecture. Expected %#08x, got %#08x.\n", |
| expected_kernel_type, kernel_header->type); |
| return false; |
| } |
| |
| // Ensure payload contains enough data for the kernel header. |
| if (kernel_header->length < sizeof(zbi_kernel_t)) { |
| ERROR("ZBI kernel payload too small.\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| bool IsValidChromeOSKernel(cpp20::span<const uint8_t> data) { |
| // Ensure the data contains the ChromeOS verification block magic |
| // signature. |
| // |
| // See https://www.chromium.org/chromium-os/chromiumos-design-docs/disk-format |
| if (data.size() < kChromeOsMagicHeader.size()) { |
| ERROR("ChromeOS kernel payload too small.\n"); |
| return false; |
| } |
| if (memcmp(data.data(), kChromeOsMagicHeader.data(), kChromeOsMagicHeader.size()) != 0) { |
| ERROR("ChromeOS kernel magic header invalid.\n"); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| } // namespace paver |