blob: 1a508dfad7026a4c0d3882ebacb469e9cf5ef52f [file] [log] [blame]
// Copyright 2018 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.
#include <object/job_policy.h>
#include <assert.h>
#include <err.h>
#include <fbl/algorithm.h>
#include <kernel/deadline.h>
#include <lib/counters.h>
#include <zircon/syscalls/policy.h>
namespace {
constexpr uint32_t kDefaultAction = ZX_POL_ACTION_ALLOW;
constexpr pol_cookie_t kPolicyEmpty = 0u;
// The encoding of the basic policy is done 4 bits per each item.
//
// - When the top bit is 1, the lower 3 bits contain the action
// (ZX_POL_ACTION_ALLOW, ZX_POL_ACTION_DENY, etc.)
//
// - When the top bit is 0 then its the default policy and other bits
// should be zero so that kPolicyEmpty == 0 meets the requirement of
// all entries being default.
union Encoding {
static constexpr uint8_t kExplicitBit = 0b1000;
static constexpr uint8_t kActionBits = 0b0111;
// Indicates the policies are fully encoded in the cookie.
static constexpr uint8_t kPolicyInCookie = 0;
pol_cookie_t encoded;
struct {
uint64_t bad_handle : 4;
uint64_t wrong_obj : 4;
uint64_t vmar_wx : 4;
uint64_t new_vmo : 4;
uint64_t new_channel : 4;
uint64_t new_event : 4;
uint64_t new_eventpair : 4;
uint64_t new_port : 4;
uint64_t new_socket : 4;
uint64_t new_fifo : 4;
uint64_t new_timer : 4;
uint64_t new_process : 4;
uint64_t new_profile : 4;
uint64_t unused_bits : 11;
uint64_t cookie_mode : 1; // see kPolicyInCookie.
};
static uint32_t action(uint64_t item) { return item & kActionBits; }
static bool is_default(uint64_t item) { return item == 0; }
};
// The packing of bits on a bitset (above) is defined by the standard as
// implementation dependent so we must check that it is using the storage
// space of a single uint64_t so the 'union' trick works.
static_assert(sizeof(Encoding) == sizeof(pol_cookie_t), "bitfield issue");
// It is critical that this array contain all "new object" policies because it's used to implement
// ZX_NEW_ANY.
const 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,
};
static_assert(
fbl::count_of(kNewObjectPolicies) + 4 == ZX_POL_MAX,
"please update JobPolicy::AddPartial, JobPolicy::QueryBasicPolicy, and kNewObjectPolicies");
bool CanSetEntry(uint64_t existing, uint32_t new_action) {
if (Encoding::is_default(existing))
return true;
return (new_action == Encoding::action(existing)) ? true : false;
}
uint32_t GetEffectiveAction(uint64_t policy) {
return Encoding::is_default(policy) ? kDefaultAction : Encoding::action(policy);
}
#define POLMAN_SET_ENTRY(mode, existing, in_pol, resultant) \
do { \
if (CanSetEntry(existing, in_pol)) { \
resultant = in_pol & Encoding::kActionBits; \
resultant |= Encoding::kExplicitBit; \
} else if (mode == ZX_JOB_POL_ABSOLUTE) { \
return ZX_ERR_ALREADY_EXISTS; \
} \
} while (0)
zx_status_t AddPartial(uint32_t mode, pol_cookie_t existing_policy,
uint32_t condition, uint32_t policy, uint64_t* partial) {
Encoding existing = {existing_policy};
Encoding result = {};
if (policy >= ZX_POL_ACTION_MAX)
return ZX_ERR_NOT_SUPPORTED;
switch (condition) {
case ZX_POL_BAD_HANDLE:
POLMAN_SET_ENTRY(mode, existing.bad_handle, policy, result.bad_handle);
break;
case ZX_POL_WRONG_OBJECT:
POLMAN_SET_ENTRY(mode, existing.wrong_obj, policy, result.wrong_obj);
break;
case ZX_POL_VMAR_WX:
POLMAN_SET_ENTRY(mode, existing.vmar_wx, policy, result.vmar_wx);
break;
case ZX_POL_NEW_VMO:
POLMAN_SET_ENTRY(mode, existing.new_vmo, policy, result.new_vmo);
break;
case ZX_POL_NEW_CHANNEL:
POLMAN_SET_ENTRY(mode, existing.new_channel, policy, result.new_channel);
break;
case ZX_POL_NEW_EVENT:
POLMAN_SET_ENTRY(mode, existing.new_event, policy, result.new_event);
break;
case ZX_POL_NEW_EVENTPAIR:
POLMAN_SET_ENTRY(mode, existing.new_eventpair, policy, result.new_eventpair);
break;
case ZX_POL_NEW_PORT:
POLMAN_SET_ENTRY(mode, existing.new_port, policy, result.new_port);
break;
case ZX_POL_NEW_SOCKET:
POLMAN_SET_ENTRY(mode, existing.new_socket, policy, result.new_socket);
break;
case ZX_POL_NEW_FIFO:
POLMAN_SET_ENTRY(mode, existing.new_fifo, policy, result.new_fifo);
break;
case ZX_POL_NEW_TIMER:
POLMAN_SET_ENTRY(mode, existing.new_timer, policy, result.new_timer);
break;
case ZX_POL_NEW_PROCESS:
POLMAN_SET_ENTRY(mode, existing.new_process, policy, result.new_process);
break;
case ZX_POL_NEW_PROFILE:
POLMAN_SET_ENTRY(mode, existing.new_profile, policy, result.new_profile);
break;
default:
return ZX_ERR_NOT_SUPPORTED;
}
*partial = result.encoded;
return ZX_OK;
}
#undef POLMAN_SET_ENTRY
} // namespace
zx_status_t JobPolicy::AddBasicPolicy(uint32_t mode,
const zx_policy_basic_t* policy_input,
size_t policy_count) {
// Don't allow overlong policies.
if (policy_count > ZX_POL_MAX) {
return ZX_ERR_OUT_OF_RANGE;
}
uint64_t partials[ZX_POL_MAX] = {};
// The policy computation algorithm is as follows:
//
// loop over all input entries
// if existing item is default or same then
// store new policy in partial result array
// else if mode is absolute exit with failure
// else continue
//
// A special case is ZX_POL_NEW_ANY which applies the algorithm with
// the same input over all ZX_NEW_ actions so that the following can
// be expressed:
//
// [0] ZX_POL_NEW_ANY --> ZX_POL_ACTION_DENY
// [1] ZX_POL_NEW_CHANNEL --> ZX_POL_ACTION_ALLOW
//
// Which means "deny all object creation except for channel".
zx_status_t res = ZX_OK;
pol_cookie_t new_cookie = cookie_;
for (size_t ix = 0; ix != policy_count; ++ix) {
const auto& in = policy_input[ix];
if (in.condition >= ZX_POL_MAX) {
return ZX_ERR_INVALID_ARGS;
}
if (in.condition == ZX_POL_NEW_ANY) {
// loop over all ZX_POL_NEW_xxxx conditions.
for (auto pol : kNewObjectPolicies) {
if ((res = AddPartial(mode, new_cookie, pol, in.policy, &partials[pol])) < 0) {
return res;
}
}
} else {
if ((res = AddPartial(
mode, new_cookie, in.condition, in.policy, &partials[in.condition])) < 0) {
return res;
}
}
}
// Compute the resultant policy. The simple OR works because the only items that
// can change are the items that have zero. See Encoding::is_default().
for (const auto& partial : partials) {
new_cookie |= partial;
}
cookie_ = new_cookie;
return ZX_OK;
}
uint32_t JobPolicy::QueryBasicPolicy(uint32_t condition) const {
if (cookie_ == kPolicyEmpty) {
return kDefaultAction;
}
Encoding existing = {cookie_};
DEBUG_ASSERT(existing.cookie_mode == Encoding::kPolicyInCookie);
switch (condition) {
case ZX_POL_BAD_HANDLE: return GetEffectiveAction(existing.bad_handle);
case ZX_POL_WRONG_OBJECT: return GetEffectiveAction(existing.wrong_obj);
case ZX_POL_NEW_VMO: return GetEffectiveAction(existing.new_vmo);
case ZX_POL_NEW_CHANNEL: return GetEffectiveAction(existing.new_channel);
case ZX_POL_NEW_EVENT: return GetEffectiveAction(existing.new_event);
case ZX_POL_NEW_EVENTPAIR: return GetEffectiveAction(existing.new_eventpair);
case ZX_POL_NEW_PORT: return GetEffectiveAction(existing.new_port);
case ZX_POL_NEW_SOCKET: return GetEffectiveAction(existing.new_socket);
case ZX_POL_NEW_FIFO: return GetEffectiveAction(existing.new_fifo);
case ZX_POL_NEW_TIMER: return GetEffectiveAction(existing.new_timer);
case ZX_POL_NEW_PROCESS: return GetEffectiveAction(existing.new_process);
case ZX_POL_NEW_PROFILE: return GetEffectiveAction(existing.new_profile);
case ZX_POL_VMAR_WX: return GetEffectiveAction(existing.vmar_wx);
default: return ZX_POL_ACTION_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 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) \
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), \
}; \
static_assert(fbl::count_of(COUNTER_ARRAY(action)) == ZX_POL_MAX);
// 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)
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