blob: c4135aad5e61f0f4ad6909dcfa6e185fc682c3af [file] [log] [blame]
// 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 <zircon/boot/image.h>
#include <zircon/errors.h>
#include <zircon/status.h>
#include <fbl/algorithm.h>
#include <fbl/alloc_checker.h>
#include <fbl/span.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);
}
// Extract the payload out of the given ZBI image.
//
// Return "true" on success, or "false" if the input data is invalid.
//
// On success, sets "header" to the header of the ZBI image, and
// "payload" to the payload of the ZBI. Both are guaranteed to be
// completed contained in "data".
bool ExtractZbiPayload(fbl::Span<const uint8_t> data, const zbi_header_t** header,
fbl::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;
}
} // namespace
bool IsValidKernelZbi(Arch arch, fbl::Span<const uint8_t> data) {
// Get container header.
const zbi_header_t* container_header;
fbl::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;
fbl::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(fbl::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