1 // Copyright 2018 The Fuchsia Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include <object/job_policy.h>
6
7 #include <assert.h>
8 #include <err.h>
9 #include <kernel/timer_slack.h>
10 #include <zircon/syscalls/policy.h>
11
12 namespace {
13
14 constexpr uint32_t kDefaultAction = ZX_POL_ACTION_ALLOW;
15
16 constexpr pol_cookie_t kPolicyEmpty = 0u;
17
18 // The encoding of the basic policy is done 4 bits per each item.
19 //
20 // - When the top bit is 1, the lower 3 bits track the action:
21 // 0 : ZX_POL_ACTION_ALLOW or not (ZX_POL_ACTION_DENY)
22 // 1 : ZX_POL_ACTION_EXCEPTION or not
23 // 2 : ZX_POL_ACTION_KILL or not
24 //
25 // - When the top bit is 0 then its the default policy and other bits
26 // should be zero so that kPolicyEmpty == 0 meets the requirement of
27 // all entries being default.
28
29 union Encoding {
30 static constexpr uint8_t kExplicitBit = 0b1000;
31 static constexpr uint8_t kActionBits = 0b0111;
32
33 // Indicates the policies are fully encoded in the cookie.
34 static constexpr uint8_t kPolicyInCookie = 0;
35
36 pol_cookie_t encoded;
37 struct {
38 uint64_t bad_handle : 4;
39 uint64_t wrong_obj : 4;
40 uint64_t vmar_wx : 4;
41 uint64_t new_vmo : 4;
42 uint64_t new_channel : 4;
43 uint64_t new_event : 4;
44 uint64_t new_eventpair : 4;
45 uint64_t new_port : 4;
46 uint64_t new_socket : 4;
47 uint64_t new_fifo : 4;
48 uint64_t new_timer : 4;
49 uint64_t new_process : 4;
50 uint64_t unused_bits : 15;
51 uint64_t cookie_mode : 1; // see kPolicyInCookie.
52 };
53
action(uint64_t item)54 static uint32_t action(uint64_t item) { return item & kActionBits; }
is_default(uint64_t item)55 static bool is_default(uint64_t item) { return item == 0; }
56 };
57
58 constexpr uint32_t kPolicyActionValidBits =
59 ZX_POL_ACTION_ALLOW | ZX_POL_ACTION_DENY | ZX_POL_ACTION_EXCEPTION | ZX_POL_ACTION_KILL;
60
61 // The packing of bits on a bitset (above) is defined by the standard as
62 // implementation dependent so we must check that it is using the storage
63 // space of a single uint64_t so the 'union' trick works.
64 static_assert(sizeof(Encoding) == sizeof(pol_cookie_t), "bitfield issue");
65
66 // Make sure that adding new policies forces updating this file.
67 static_assert(ZX_POL_MAX == 13u, "please update PolicyManager AddPolicy and QueryBasicPolicy");
68
CanSetEntry(uint64_t existing,uint32_t new_action)69 bool CanSetEntry(uint64_t existing, uint32_t new_action) {
70 if (Encoding::is_default(existing))
71 return true;
72 return (new_action == Encoding::action(existing)) ? true : false;
73 }
74
GetEffectiveAction(uint64_t policy)75 uint32_t GetEffectiveAction(uint64_t policy) {
76 return Encoding::is_default(policy) ? kDefaultAction : Encoding::action(policy);
77 }
78
79 #define POLMAN_SET_ENTRY(mode, existing, in_pol, resultant) \
80 do { \
81 if (CanSetEntry(existing, in_pol)) { \
82 resultant = in_pol & Encoding::kActionBits; \
83 resultant |= Encoding::kExplicitBit; \
84 } else if (mode == ZX_JOB_POL_ABSOLUTE) { \
85 return ZX_ERR_ALREADY_EXISTS; \
86 } \
87 } while (0)
88
AddPartial(uint32_t mode,pol_cookie_t existing_policy,uint32_t condition,uint32_t policy,uint64_t * partial)89 zx_status_t AddPartial(uint32_t mode, pol_cookie_t existing_policy,
90 uint32_t condition, uint32_t policy, uint64_t* partial) {
91 Encoding existing = {existing_policy};
92 Encoding result = {};
93
94 if (policy & ~kPolicyActionValidBits)
95 return ZX_ERR_NOT_SUPPORTED;
96
97 switch (condition) {
98 case ZX_POL_BAD_HANDLE:
99 POLMAN_SET_ENTRY(mode, existing.bad_handle, policy, result.bad_handle);
100 break;
101 case ZX_POL_WRONG_OBJECT:
102 POLMAN_SET_ENTRY(mode, existing.wrong_obj, policy, result.wrong_obj);
103 break;
104 case ZX_POL_VMAR_WX:
105 POLMAN_SET_ENTRY(mode, existing.vmar_wx, policy, result.vmar_wx);
106 break;
107 case ZX_POL_NEW_VMO:
108 POLMAN_SET_ENTRY(mode, existing.new_vmo, policy, result.new_vmo);
109 break;
110 case ZX_POL_NEW_CHANNEL:
111 POLMAN_SET_ENTRY(mode, existing.new_channel, policy, result.new_channel);
112 break;
113 case ZX_POL_NEW_EVENT:
114 POLMAN_SET_ENTRY(mode, existing.new_event, policy, result.new_event);
115 break;
116 case ZX_POL_NEW_EVENTPAIR:
117 POLMAN_SET_ENTRY(mode, existing.new_eventpair, policy, result.new_eventpair);
118 break;
119 case ZX_POL_NEW_PORT:
120 POLMAN_SET_ENTRY(mode, existing.new_port, policy, result.new_port);
121 break;
122 case ZX_POL_NEW_SOCKET:
123 POLMAN_SET_ENTRY(mode, existing.new_socket, policy, result.new_socket);
124 break;
125 case ZX_POL_NEW_FIFO:
126 POLMAN_SET_ENTRY(mode, existing.new_fifo, policy, result.new_fifo);
127 break;
128 case ZX_POL_NEW_TIMER:
129 POLMAN_SET_ENTRY(mode, existing.new_timer, policy, result.new_timer);
130 break;
131 case ZX_POL_NEW_PROCESS:
132 POLMAN_SET_ENTRY(mode, existing.new_process, policy, result.new_process);
133 break;
134 default:
135 return ZX_ERR_NOT_SUPPORTED;
136 }
137
138 *partial = result.encoded;
139 return ZX_OK;
140 }
141
142 #undef POLMAN_SET_ENTRY
143
144 } // namespace
145
146
AddBasicPolicy(uint32_t mode,const zx_policy_basic_t * policy_input,size_t policy_count)147 zx_status_t JobPolicy::AddBasicPolicy(uint32_t mode,
148 const zx_policy_basic_t* policy_input,
149 size_t policy_count) {
150 // Don't allow overlong policies.
151 if (policy_count > ZX_POL_MAX) {
152 return ZX_ERR_OUT_OF_RANGE;
153 }
154
155 uint64_t partials[ZX_POL_MAX] = {};
156
157 // The policy computation algorithm is as follows:
158 //
159 // loop over all input entries
160 // if existing item is default or same then
161 // store new policy in partial result array
162 // else if mode is absolute exit with failure
163 // else continue
164 //
165 // A special case is ZX_POL_NEW_ANY which applies the algorithm with
166 // the same input over all ZX_NEW_ actions so that the following can
167 // be expressed:
168 //
169 // [0] ZX_POL_NEW_ANY --> ZX_POL_ACTION_DENY
170 // [1] ZX_POL_NEW_CHANNEL --> ZX_POL_ACTION_ALLOW
171 //
172 // Which means "deny all object creation except for channel".
173
174 zx_status_t res = ZX_OK;
175
176 pol_cookie_t new_cookie = cookie_;
177
178 for (size_t ix = 0; ix != policy_count; ++ix) {
179 const auto& in = policy_input[ix];
180
181 if (in.condition >= ZX_POL_MAX) {
182 return ZX_ERR_INVALID_ARGS;
183 }
184
185 if (in.condition == ZX_POL_NEW_ANY) {
186 // loop over all ZX_POL_NEW_xxxx conditions.
187 for (uint32_t it = ZX_POL_NEW_VMO; it <= ZX_POL_NEW_TIMER; ++it) {
188 if ((res = AddPartial(mode, new_cookie, it, in.policy, &partials[it])) < 0) {
189 return res;
190 }
191 }
192 } else {
193 if ((res = AddPartial(
194 mode, new_cookie, in.condition, in.policy, &partials[in.condition])) < 0) {
195 return res;
196 }
197 }
198 }
199
200 // Compute the resultant policy. The simple OR works because the only items that
201 // can change are the items that have zero. See Encoding::is_default().
202 for (const auto& partial : partials) {
203 new_cookie |= partial;
204 }
205
206 cookie_ = new_cookie;
207 return ZX_OK;
208 }
209
QueryBasicPolicy(uint32_t condition) const210 uint32_t JobPolicy::QueryBasicPolicy(uint32_t condition) const {
211 if (cookie_ == kPolicyEmpty) {
212 return kDefaultAction;
213 }
214
215 Encoding existing = {cookie_};
216 DEBUG_ASSERT(existing.cookie_mode == Encoding::kPolicyInCookie);
217
218 switch (condition) {
219 case ZX_POL_BAD_HANDLE: return GetEffectiveAction(existing.bad_handle);
220 case ZX_POL_WRONG_OBJECT: return GetEffectiveAction(existing.wrong_obj);
221 case ZX_POL_NEW_VMO: return GetEffectiveAction(existing.new_vmo);
222 case ZX_POL_NEW_CHANNEL: return GetEffectiveAction(existing.new_channel);
223 case ZX_POL_NEW_EVENT: return GetEffectiveAction(existing.new_event);
224 case ZX_POL_NEW_EVENTPAIR: return GetEffectiveAction(existing.new_eventpair);
225 case ZX_POL_NEW_PORT: return GetEffectiveAction(existing.new_port);
226 case ZX_POL_NEW_SOCKET: return GetEffectiveAction(existing.new_socket);
227 case ZX_POL_NEW_FIFO: return GetEffectiveAction(existing.new_fifo);
228 case ZX_POL_NEW_TIMER: return GetEffectiveAction(existing.new_timer);
229 case ZX_POL_NEW_PROCESS: return GetEffectiveAction(existing.new_process);
230 case ZX_POL_VMAR_WX: return GetEffectiveAction(existing.vmar_wx);
231 default: return ZX_POL_ACTION_DENY;
232 }
233 }
234
SetTimerSlack(TimerSlack slack)235 void JobPolicy::SetTimerSlack(TimerSlack slack) {
236 slack_ = slack;
237 }
238
GetTimerSlack() const239 TimerSlack JobPolicy::GetTimerSlack() const {
240 return slack_;
241 }
242
operator ==(const JobPolicy & rhs) const243 bool JobPolicy::operator==(const JobPolicy& rhs) const {
244 if (this == &rhs) {
245 return true;
246 }
247
248 return cookie_ == rhs.cookie_ &&
249 slack_ == rhs.slack_;
250 }
251
operator !=(const JobPolicy & rhs) const252 bool JobPolicy::operator!=(const JobPolicy& rhs) const {
253 return !operator==(rhs);
254 }
255