blob: 9ccbdd1e1cdb77548b5b112c3116828a7e4a2caf [file] [log] [blame] [edit]
// Copyright 2018 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/job_policy.h"
#include <assert.h>
#include <lib/counters.h>
#include <zircon/errors.h>
#include <zircon/syscalls/policy.h>
#include <fbl/algorithm.h>
#include <fbl/bits.h>
#include <kernel/deadline.h>
#include <ktl/iterator.h>
#include <ktl/enforce.h>
namespace {
// It is critical that this array contain all "new object" policies because it's used to implement
// ZX_NEW_ANY.
constexpr uint32_t kNewObjectPolicies[]{
ZX_POL_NEW_VMO, ZX_POL_NEW_CHANNEL, ZX_POL_NEW_EVENT, ZX_POL_NEW_EVENTPAIR,
ZX_POL_NEW_PORT, ZX_POL_NEW_SOCKET, ZX_POL_NEW_FIFO, ZX_POL_NEW_TIMER,
ZX_POL_NEW_PROCESS, ZX_POL_NEW_PROFILE, ZX_POL_NEW_PAGER, ZX_POL_NEW_IOB,
};
static_assert(
ktl::size(kNewObjectPolicies) + 5 == ZX_POL_MAX,
"please update JobPolicy::AddPartial, JobPolicy::QueryBasicPolicy, kNewObjectPolicies,"
"and the add_basic_policy_deny_any_new() test");
bool PolicyOverrideIsValid(uint32_t override) {
switch (override) {
case ZX_POL_OVERRIDE_DENY:
case ZX_POL_OVERRIDE_ALLOW:
return true;
default:
return false;
}
}
zx_status_t AddPartial(uint32_t mode, uint32_t condition, uint32_t action, uint32_t override,
JobPolicyCollection& bits) {
if (action >= ZX_POL_ACTION_MAX) {
return ZX_ERR_NOT_SUPPORTED;
}
if (!PolicyOverrideIsValid(override)) {
return ZX_ERR_INVALID_ARGS;
}
if (condition >= ZX_POL_MAX || condition == ZX_POL_NEW_ANY) {
return ZX_ERR_INVALID_ARGS;
}
bool override_bit = override == ZX_POL_OVERRIDE_ALLOW;
auto condition_bits = bits[condition];
if (condition_bits.override()) {
condition_bits.set_action(action);
condition_bits.set_override(override_bit);
return ZX_OK;
}
if (condition_bits.action() == action && !override_bit) {
return ZX_OK;
}
return (mode == ZX_JOB_POL_ABSOLUTE) ? ZX_ERR_ALREADY_EXISTS : ZX_OK;
}
} // namespace
JobPolicy::JobPolicy(const JobPolicy& parent)
: collection_(parent.collection_), slack_(parent.slack_) {}
JobPolicy::JobPolicy(JobPolicyCollection collection, const TimerSlack& slack)
: collection_(collection), slack_(slack) {}
// static
JobPolicy JobPolicy::CreateRootPolicy() {
static_assert((ZX_POL_ACTION_ALLOW == 0u) && (ZX_POL_OVERRIDE_ALLOW == 0u));
return JobPolicy({}, TimerSlack::none());
}
zx_status_t JobPolicy::AddBasicPolicy(uint32_t mode, const zx_policy_basic_v2_t* policy_input,
size_t policy_count) {
// Don't allow overlong policies.
if (policy_count > ZX_POL_MAX) {
return ZX_ERR_OUT_OF_RANGE;
}
zx_status_t status = ZX_OK;
JobPolicyCollection updated_collection = collection_;
bool has_new_any = false;
uint32_t new_any_override = 0;
for (size_t ix = 0; ix != policy_count; ++ix) {
const auto& in = policy_input[ix];
if (in.condition == ZX_POL_NEW_ANY) {
for (auto cond : kNewObjectPolicies) {
if (status = AddPartial(mode, cond, in.action, ZX_POL_OVERRIDE_ALLOW, updated_collection);
status != ZX_OK) {
return status;
}
}
has_new_any = true;
new_any_override = in.flags;
} else if (status = AddPartial(mode, in.condition, in.action, in.flags, updated_collection);
status != ZX_OK) {
return status;
}
}
if (has_new_any) {
if (!PolicyOverrideIsValid(new_any_override)) {
return ZX_ERR_INVALID_ARGS;
}
bool override_bit = new_any_override == ZX_POL_OVERRIDE_ALLOW;
for (auto cond : kNewObjectPolicies) {
updated_collection[cond].set_override(override_bit);
}
}
collection_ = updated_collection;
return ZX_OK;
}
uint32_t JobPolicy::QueryBasicPolicy(uint32_t condition) const {
if (condition >= ZX_POL_MAX || condition == ZX_POL_NEW_ANY) [[unlikely]] {
return ZX_POL_ACTION_DENY;
}
// The following const_cast allows us to reuse the JobPolicyCollection without having to resort to
// template-ing over const.
return const_cast<JobPolicy*>(this)->collection_[condition].action();
}
uint32_t JobPolicy::QueryBasicPolicyOverride(uint32_t condition) const {
if (condition >= ZX_POL_MAX || condition == ZX_POL_NEW_ANY) [[unlikely]] {
return ZX_POL_OVERRIDE_DENY;
}
// The following const_cast allows us to reuse the JobPolicyCollection without having to resort to
// template-ing over const.
return const_cast<JobPolicy*>(this)->collection_[condition].override() ? ZX_POL_OVERRIDE_ALLOW
: ZX_POL_OVERRIDE_DENY;
}
void JobPolicy::SetTimerSlack(TimerSlack slack) { slack_ = slack; }
TimerSlack JobPolicy::GetTimerSlack() const { return slack_; }
bool JobPolicy::operator==(const JobPolicy& rhs) const {
if (this == &rhs) {
return true;
}
return collection_ == rhs.collection_ && slack_ == rhs.slack_;
}
bool JobPolicy::operator!=(const JobPolicy& rhs) const { return !operator==(rhs); }
// Evaluates to the name of the kcounter for the given action and condition.
//
// E.g. COUNTER(deny, new_channel) -> policy_action_deny_new_channel_count
#define COUNTER(action, condition) policy_##action##_##condition##_count
// Defines a kcounter for the given action and condition.
#define DEFINE_COUNTER(action, condition) \
KCOUNTER(COUNTER(action, condition), "policy." #action "." #condition)
// Evaluates to the name of an array of pointers to Counter objects.
//
// See DEFINE_COUNTER_ARRAY for details.
#define COUNTER_ARRAY(action) counters_##action
// Defines kcounters for the given action and creates an array named |COUNTER_ARRAY(action)|.
//
// The array has length ZX_POL_MAX and contains pointers to the Counter objects. The array should be
// indexed by condition. Note, some array elements may be null.
//
// Example:
//
// DEFINE_COUNTER_ARRAY(deny);
// kcounter_add(*COUNTER_ARRAY(deny)[ZX_POL_NEW_CHANNEL], 1);
//
#define DEFINE_COUNTER_ARRAY(action) \
DEFINE_COUNTER(action, bad_handle) \
DEFINE_COUNTER(action, wrong_object) \
DEFINE_COUNTER(action, vmar_wx) \
DEFINE_COUNTER(action, new_vmo) \
DEFINE_COUNTER(action, new_channel) \
DEFINE_COUNTER(action, new_event) \
DEFINE_COUNTER(action, new_eventpair) \
DEFINE_COUNTER(action, new_port) \
DEFINE_COUNTER(action, new_socket) \
DEFINE_COUNTER(action, new_fifo) \
DEFINE_COUNTER(action, new_timer) \
DEFINE_COUNTER(action, new_process) \
DEFINE_COUNTER(action, new_profile) \
DEFINE_COUNTER(action, new_pager) \
DEFINE_COUNTER(action, ambient_mark_vmo_exec) \
DEFINE_COUNTER(action, new_iob) \
static constexpr const Counter* const COUNTER_ARRAY(action)[] = { \
[ZX_POL_BAD_HANDLE] = &COUNTER(action, bad_handle), \
[ZX_POL_WRONG_OBJECT] = &COUNTER(action, wrong_object), \
[ZX_POL_VMAR_WX] = &COUNTER(action, vmar_wx), \
[ZX_POL_NEW_ANY] = nullptr, /* ZX_POL_NEW_ANY is a pseudo condition */ \
[ZX_POL_NEW_VMO] = &COUNTER(action, new_vmo), \
[ZX_POL_NEW_CHANNEL] = &COUNTER(action, new_channel), \
[ZX_POL_NEW_EVENT] = &COUNTER(action, new_event), \
[ZX_POL_NEW_EVENTPAIR] = &COUNTER(action, new_eventpair), \
[ZX_POL_NEW_PORT] = &COUNTER(action, new_port), \
[ZX_POL_NEW_SOCKET] = &COUNTER(action, new_socket), \
[ZX_POL_NEW_FIFO] = &COUNTER(action, new_fifo), \
[ZX_POL_NEW_TIMER] = &COUNTER(action, new_timer), \
[ZX_POL_NEW_PROCESS] = &COUNTER(action, new_process), \
[ZX_POL_NEW_PROFILE] = &COUNTER(action, new_profile), \
[ZX_POL_NEW_PAGER] = &COUNTER(action, new_pager), \
[ZX_POL_AMBIENT_MARK_VMO_EXEC] = &COUNTER(action, ambient_mark_vmo_exec), \
[ZX_POL_NEW_IOB] = &COUNTER(action, new_iob), \
}; \
static_assert(ktl::size(COUNTER_ARRAY(action)) == ZX_POL_MAX);
#if defined(__clang__)
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wc99-designator"
#endif
// Counts policy violations resulting in ZX_POL_ACTION_DENY or ZX_POL_ACTION_DENY_EXCEPTION.
DEFINE_COUNTER_ARRAY(deny)
// Counts policy violations resulting in ZX_POL_ACTION_KILL.
DEFINE_COUNTER_ARRAY(kill)
#if defined(__clang__)
#pragma GCC diagnostic pop
#endif
static_assert(ZX_POL_ACTION_MAX == 5, "add another instantiation of DEFINE_COUNTER_ARRAY");
void JobPolicy::IncrementCounter(uint32_t action, uint32_t condition) {
DEBUG_ASSERT(action < ZX_POL_ACTION_MAX);
DEBUG_ASSERT(condition < ZX_POL_MAX);
const Counter* counter = nullptr;
switch (action) {
case ZX_POL_ACTION_DENY:
case ZX_POL_ACTION_DENY_EXCEPTION:
counter = COUNTER_ARRAY(deny)[condition];
break;
case ZX_POL_ACTION_KILL:
counter = COUNTER_ARRAY(kill)[condition];
break;
};
if (!counter) {
return;
}
kcounter_add(*counter, 1);
}
#undef COUNTER
#undef DEFINE_COUNTER
#undef COUNTER_ARRAY
#undef DEFINE_COUNTER_ARRAY