blob: bb75d03670bf8acaac267c940bd5cb651c43cd76 [file] [log] [blame]
// Copyright 2017 The Fuchsia Authors
//
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file or at
// https://opensource.org/licenses/MIT
#include "object/resource.h"
#include <align.h>
#include <lib/root_resource_filter.h>
#include <trace.h>
#include <zircon/syscalls/resource.h>
#include <fbl/ref_ptr.h>
#include <kernel/range_check.h>
#include <object/process_dispatcher.h>
#include <object/resource_dispatcher.h>
#define LOCAL_TRACE 0
// TODO(fxbug.dev/32272): Take another look at validation and consider returning
// dispatchers or move validation into the parent dispatcher itself.
// Check if the resource referenced by |handle| is of kind |kind|, or ZX_RSRC_KIND_ROOT.
//
// Possible errors:
// ++ ZX_ERR_ACCESS_DENIED: |handle| is not the right |kind| of handle.
// ++ ZX_ERR_WRONG_TYPE: |handle| is not a valid handle.
zx_status_t validate_resource(zx_handle_t handle, zx_rsrc_kind_t kind) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ResourceDispatcher> resource;
auto status = up->handle_table().GetDispatcher(handle, &resource);
if (status != ZX_OK) {
return status;
}
auto res_kind = resource->get_kind();
if (res_kind == kind || res_kind == ZX_RSRC_KIND_ROOT) {
return ZX_OK;
}
return ZX_ERR_WRONG_TYPE;
}
zx_status_t validate_ranged_resource(fbl::RefPtr<ResourceDispatcher> resource, zx_rsrc_kind_t kind,
uintptr_t base, size_t size) {
// Root gets access to almost everything, but there are still resource ranges
// it is not permitted to mint. For example:
//
// 1) All of physical RAM is off limits (with limited platform specific
// exceptions). It exists on the CPU accessible physical bus (so, the
// domain controlled by ZX_RSRC_KIND_MMIO) and user mode program should not
// be able to request access to physical RAM by address, they should be
// forced to go through the PMM using VMO creation instead.
// 2) Any MMIO accessible interrupt controller registers.
// 3) Any MMIO accessible IOMMU registers.
//
// Enforce that policy here by disallowing resource minting for any request
// which touches any disallowed ranges.
//
if (resource->get_kind() == ZX_RSRC_KIND_ROOT || resource->IsRangedRoot(kind)) {
if (!root_resource_filter_can_access_region(base, size, kind)) {
return ZX_ERR_ACCESS_DENIED;
}
return ZX_OK;
}
if (resource->get_kind() != kind) {
return ZX_ERR_WRONG_TYPE;
}
uint64_t rbase = resource->get_base();
size_t rsize = resource->get_size();
// In the specific case of MMIO, everything is rounded to PAGE_SIZE units
// because it's the smallest unit we can operate at with the MMU.
if (resource->get_kind() == ZX_RSRC_KIND_MMIO) {
const uint64_t aligned_rbase = ROUNDDOWN(rbase, PAGE_SIZE);
rsize = PAGE_ALIGN((rbase - aligned_rbase) + rsize);
rbase = aligned_rbase;
}
LTRACEF("req [base %#lx size %#lx] and resource [base %#lx size %#lx]\n", base, size, rbase,
rsize);
// All resources need to track their lineage back to the root resource,
// and the root resource is specifically prohibited from producing ranges
// which intersect anything in the deny list. Since all resource ranges
// need to be a subset of their parent, it should be impossible for a
// resource object to exist with a range which intersects anything in the
// deny list. Check that with a debug assert here.
ZX_DEBUG_ASSERT(root_resource_filter_can_access_region(rbase, rsize, kind));
// Check for intersection and make sure the requested base+size fits within
// the resource's address space allocation.
uintptr_t ibase;
size_t isize;
if (!GetIntersect(base, size, rbase, rsize, &ibase, &isize) || isize != size || ibase != base) {
return ZX_ERR_OUT_OF_RANGE;
}
return ZX_OK;
}
// Check if the resource referenced by |handle| is of kind |kind|, or ZX_RSRC_KIND_ROOT. If
// |kind| matches the resource's kind, then range validation between |base| and |size| will
// be made against the resource's backing address space allocation.
//
// Possible errors:
// ++ ZX_ERR_ACCESS_DENIED: |handle| is not a valid handle.
// ++ ZX_ERR_WRONG_TYPE: |handle| is not a valid resource handle, or |kind| is invalid for
// the request.
// ++ ZX_ERR_OUT_OF_RANGE: The range specified by |base| and |Len| is not granted by this
// resource.
zx_status_t validate_ranged_resource(zx_handle_t handle, zx_rsrc_kind_t kind, uintptr_t base,
size_t size) {
auto up = ProcessDispatcher::GetCurrent();
fbl::RefPtr<ResourceDispatcher> resource;
auto status = up->handle_table().GetDispatcher(handle, &resource);
if (status != ZX_OK) {
return status;
}
return validate_ranged_resource(resource, kind, base, size);
}