blob: c83543c2cdcabde85fb8fdce9500626f08922d4d [file] [log] [blame]
// 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");
FBL_BITFIELD_DEF_START(JobPolicyBits, uint64_t)
FBL_BITFIELD_MEMBER(bad_handle, 0, 3);
FBL_BITFIELD_MEMBER(bad_handle_override, 3, 1);
FBL_BITFIELD_MEMBER(wrong_object, 4, 3);
FBL_BITFIELD_MEMBER(wrong_object_override, 7, 1);
FBL_BITFIELD_MEMBER(vmar_wx, 8, 3);
FBL_BITFIELD_MEMBER(vmar_wx_override, 11, 1);
FBL_BITFIELD_MEMBER(new_vmo, 12, 3);
FBL_BITFIELD_MEMBER(new_vmo_override, 15, 1);
FBL_BITFIELD_MEMBER(new_channel, 16, 3);
FBL_BITFIELD_MEMBER(new_channel_override, 19, 1);
FBL_BITFIELD_MEMBER(new_event, 20, 3);
FBL_BITFIELD_MEMBER(new_event_override, 23, 1);
FBL_BITFIELD_MEMBER(new_eventpair, 24, 3);
FBL_BITFIELD_MEMBER(new_eventpair_override, 27, 1);
FBL_BITFIELD_MEMBER(new_port, 28, 3);
FBL_BITFIELD_MEMBER(new_port_override, 31, 1);
FBL_BITFIELD_MEMBER(new_socket, 32, 3);
FBL_BITFIELD_MEMBER(new_socket_override, 35, 1);
FBL_BITFIELD_MEMBER(new_fifo, 36, 3);
FBL_BITFIELD_MEMBER(new_fifo_override, 39, 1);
FBL_BITFIELD_MEMBER(new_timer, 40, 3);
FBL_BITFIELD_MEMBER(new_timer_override, 43, 1);
FBL_BITFIELD_MEMBER(new_process, 44, 3);
FBL_BITFIELD_MEMBER(new_process_override, 47, 1);
FBL_BITFIELD_MEMBER(new_profile, 48, 3);
FBL_BITFIELD_MEMBER(new_profile_override, 51, 1);
FBL_BITFIELD_MEMBER(new_pager, 52, 3);
FBL_BITFIELD_MEMBER(new_pager_override, 55, 1);
FBL_BITFIELD_MEMBER(ambient_mark_vmo_exec, 56, 3);
FBL_BITFIELD_MEMBER(ambient_mark_vmo_exec_override, 59, 1);
FBL_BITFIELD_MEMBER(new_iob, 60, 3);
FBL_BITFIELD_MEMBER(new_iob_override, 63, 1);
FBL_BITFIELD_MEMBER(unused_bits, 64, 0);
FBL_BITFIELD_DEF_END();
template <uint32_t condition>
struct FieldSelector {};
#define FIELD_SELECTOR_DEF(id, name) \
template <> \
struct FieldSelector<id> { \
static auto& Action(JobPolicyBits* jpb) { return jpb->name; } \
static auto& Override(JobPolicyBits* jpb) { return jpb->name##_override; } \
}
FIELD_SELECTOR_DEF(ZX_POL_BAD_HANDLE, bad_handle);
FIELD_SELECTOR_DEF(ZX_POL_WRONG_OBJECT, wrong_object);
FIELD_SELECTOR_DEF(ZX_POL_VMAR_WX, vmar_wx);
FIELD_SELECTOR_DEF(ZX_POL_NEW_VMO, new_vmo);
FIELD_SELECTOR_DEF(ZX_POL_NEW_CHANNEL, new_channel);
FIELD_SELECTOR_DEF(ZX_POL_NEW_EVENT, new_event);
FIELD_SELECTOR_DEF(ZX_POL_NEW_EVENTPAIR, new_eventpair);
FIELD_SELECTOR_DEF(ZX_POL_NEW_PORT, new_port);
FIELD_SELECTOR_DEF(ZX_POL_NEW_SOCKET, new_socket);
FIELD_SELECTOR_DEF(ZX_POL_NEW_FIFO, new_fifo);
FIELD_SELECTOR_DEF(ZX_POL_NEW_TIMER, new_timer);
FIELD_SELECTOR_DEF(ZX_POL_NEW_PROCESS, new_process);
FIELD_SELECTOR_DEF(ZX_POL_NEW_PROFILE, new_profile);
FIELD_SELECTOR_DEF(ZX_POL_NEW_PAGER, new_pager);
FIELD_SELECTOR_DEF(ZX_POL_AMBIENT_MARK_VMO_EXEC, ambient_mark_vmo_exec);
FIELD_SELECTOR_DEF(ZX_POL_NEW_IOB, new_iob);
#define CASE_RETURN_ACTION(id, bits) \
case id: \
return FieldSelector<id>::Action(bits)
#define CASE_RETURN_OVERRIDE(id, bits) \
case id: \
return FieldSelector<id>::Override(bits)
#define CASE_SET_OVERRIDE(id, bits, override) \
case id: \
FieldSelector<id>::Override(bits) = override; \
break
#define CASE_SET_ENTRY(id, bits, action, override) \
case id: { \
if (FieldSelector<id>::Override(bits) == ZX_POL_OVERRIDE_ALLOW) { \
FieldSelector<id>::Action(bits) = action; \
FieldSelector<id>::Override(bits) = override; \
return ZX_OK; \
} \
if ((FieldSelector<id>::Action(bits) == action) && (override == ZX_POL_OVERRIDE_DENY)) \
return ZX_OK; \
break; \
}
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,
JobPolicyBits* bits) {
if (action >= ZX_POL_ACTION_MAX)
return ZX_ERR_NOT_SUPPORTED;
if (!PolicyOverrideIsValid(override))
return ZX_ERR_INVALID_ARGS;
switch (condition) {
CASE_SET_ENTRY(ZX_POL_BAD_HANDLE, bits, action, override);
CASE_SET_ENTRY(ZX_POL_WRONG_OBJECT, bits, action, override);
CASE_SET_ENTRY(ZX_POL_VMAR_WX, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_VMO, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_CHANNEL, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_EVENT, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_EVENTPAIR, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_PORT, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_SOCKET, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_FIFO, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_TIMER, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_PROCESS, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_PROFILE, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_PAGER, bits, action, override);
CASE_SET_ENTRY(ZX_POL_AMBIENT_MARK_VMO_EXEC, bits, action, override);
CASE_SET_ENTRY(ZX_POL_NEW_IOB, bits, action, override);
default:
return ZX_ERR_INVALID_ARGS;
}
// If we are here setting policy failed, which migth not matter
// if it is a relative (best effort) mode.
return (mode == ZX_JOB_POL_ABSOLUTE) ? ZX_ERR_ALREADY_EXISTS : ZX_OK;
}
zx_status_t SetOverride(JobPolicyBits* policy, uint32_t condition, uint32_t override) {
if (!PolicyOverrideIsValid(override))
return ZX_ERR_INVALID_ARGS;
switch (condition) {
CASE_SET_OVERRIDE(ZX_POL_BAD_HANDLE, policy, override);
CASE_SET_OVERRIDE(ZX_POL_WRONG_OBJECT, policy, override);
CASE_SET_OVERRIDE(ZX_POL_VMAR_WX, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_VMO, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_CHANNEL, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_EVENT, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_EVENTPAIR, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_PORT, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_SOCKET, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_FIFO, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_TIMER, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_PROCESS, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_PROFILE, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_PAGER, policy, override);
CASE_SET_OVERRIDE(ZX_POL_AMBIENT_MARK_VMO_EXEC, policy, override);
CASE_SET_OVERRIDE(ZX_POL_NEW_IOB, policy, override);
default:
return ZX_ERR_INVALID_ARGS;
}
return ZX_OK;
}
uint64_t GetAction(JobPolicyBits policy, uint32_t condition) {
switch (condition) {
CASE_RETURN_ACTION(ZX_POL_BAD_HANDLE, &policy);
CASE_RETURN_ACTION(ZX_POL_WRONG_OBJECT, &policy);
CASE_RETURN_ACTION(ZX_POL_VMAR_WX, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_VMO, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_CHANNEL, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_EVENT, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_EVENTPAIR, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_PORT, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_SOCKET, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_FIFO, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_TIMER, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_PROCESS, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_PROFILE, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_PAGER, &policy);
CASE_RETURN_ACTION(ZX_POL_AMBIENT_MARK_VMO_EXEC, &policy);
CASE_RETURN_ACTION(ZX_POL_NEW_IOB, &policy);
default:
return ZX_POL_ACTION_DENY;
}
}
uint64_t GetOverride(JobPolicyBits policy, uint32_t condition) {
switch (condition) {
CASE_RETURN_OVERRIDE(ZX_POL_BAD_HANDLE, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_WRONG_OBJECT, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_VMAR_WX, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_VMO, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_CHANNEL, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_EVENT, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_EVENTPAIR, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_PORT, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_SOCKET, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_FIFO, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_TIMER, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_PROCESS, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_PROFILE, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_PAGER, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_AMBIENT_MARK_VMO_EXEC, &policy);
CASE_RETURN_OVERRIDE(ZX_POL_NEW_IOB, &policy);
default:
return ZX_POL_OVERRIDE_DENY;
}
}
#undef CASE_RETURN_ACTION
#undef CASE_RETURN_OVERRIDE
#undef CASE_SET_OVERRIDE
#undef CASE_SET_ENTRY
} // namespace
JobPolicy::JobPolicy(const JobPolicy& parent) : cookie_(parent.cookie_), slack_(parent.slack_) {}
JobPolicy::JobPolicy(pol_cookie_t cookie, const TimerSlack& slack)
: cookie_(cookie), slack_(slack) {}
// static
JobPolicy JobPolicy::CreateRootPolicy() {
static_assert((ZX_POL_ACTION_ALLOW == 0u) && (ZX_POL_OVERRIDE_ALLOW == 0u));
constexpr JobPolicyBits policy(0u);
return JobPolicy(policy.value, 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;
JobPolicyBits pol_new(cookie_);
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) {
status = AddPartial(mode, cond, in.action, ZX_POL_OVERRIDE_ALLOW, &pol_new);
if (status != ZX_OK)
return status;
}
has_new_any = true;
new_any_override = in.flags;
} else {
status = AddPartial(mode, in.condition, in.action, in.flags, &pol_new);
if (status != ZX_OK)
return status;
}
}
if (has_new_any) {
for (auto cond : kNewObjectPolicies) {
status = SetOverride(&pol_new, cond, new_any_override);
if (status != ZX_OK)
return status;
}
}
cookie_ = pol_new.value;
return ZX_OK;
}
uint32_t JobPolicy::QueryBasicPolicy(uint32_t condition) const {
JobPolicyBits policy(cookie_);
return static_cast<uint32_t>(GetAction(policy, condition));
}
uint32_t JobPolicy::QueryBasicPolicyOverride(uint32_t condition) const {
JobPolicyBits policy(cookie_);
return static_cast<uint32_t>(GetOverride(policy, condition));
}
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 cookie_ == rhs.cookie_ && 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