1 // Copyright 2016 The Fuchsia Authors
2 //
3 // Use of this source code is governed by a MIT-style
4 // license that can be found in the LICENSE file or at
5 // https://opensource.org/licenses/MIT
6
7 #include <inttypes.h>
8
9 #include <object/job_dispatcher.h>
10
11 #include <err.h>
12
13 #include <zircon/rights.h>
14 #include <zircon/syscalls/policy.h>
15
16 #include <fbl/alloc_checker.h>
17 #include <fbl/array.h>
18 #include <fbl/auto_lock.h>
19 #include <fbl/mutex.h>
20
21 #include <object/process_dispatcher.h>
22
23 #include <platform.h>
24
25 // The starting max_height value of the root job.
26 static constexpr uint32_t kRootJobMaxHeight = 32;
27
28 static constexpr char kRootJobName[] = "<superroot>";
29
30 template <>
ChildCountLocked() const31 uint32_t JobDispatcher::ChildCountLocked<JobDispatcher>() const {
32 return job_count_;
33 }
34
35 template <>
ChildCountLocked() const36 uint32_t JobDispatcher::ChildCountLocked<ProcessDispatcher>() const {
37 return process_count_;
38 }
39
40 // Calls the provided |zx_status_t func(fbl::RefPtr<DISPATCHER_TYPE>)|
41 // function on all live elements of |children|, which must be one of |jobs_|
42 // or |procs_|. Stops iterating early if |func| returns a value other than
43 // ZX_OK, returning that value from this method. |lock_| must be held when
44 // calling this method, and it will still be held while the callback is
45 // called.
46 //
47 // The returned |LiveRefsArray| needs to be destructed when |lock_| is not
48 // held anymore. The recommended pattern is:
49 //
50 // LiveRefsArray refs;
51 // {
52 // Guard<fbl::Mutex> guard{get_lock()};
53 // refs = ForEachChildInLocked(...);
54 // }
55 //
56 template <typename T, typename Fn>
ForEachChildInLocked(T & children,zx_status_t * result,Fn func)57 JobDispatcher::LiveRefsArray JobDispatcher::ForEachChildInLocked(
58 T& children, zx_status_t* result, Fn func) {
59 // Convert child raw pointers into RefPtrs. This is tricky and requires
60 // special logic on the RefPtr class to handle a ref count that can be
61 // zero.
62 //
63 // The main requirement is that |lock_| is both controlling child
64 // list lookup and also making sure that the child destructor cannot
65 // make progress when doing so. In other words, when inspecting the
66 // |children| list we can be sure that a given child process or child
67 // job is either
68 // - alive, with refcount > 0
69 // - in destruction process but blocked, refcount == 0
70
71 const uint32_t count = ChildCountLocked<typename T::ValueType>();
72
73 if (!count) {
74 *result = ZX_OK;
75 return LiveRefsArray();
76 }
77
78 fbl::AllocChecker ac;
79 LiveRefsArray refs(new (&ac) fbl::RefPtr<Dispatcher>[count], count);
80 if (!ac.check()) {
81 *result = ZX_ERR_NO_MEMORY;
82 return LiveRefsArray();
83 }
84
85 size_t ix = 0;
86
87 for (auto& craw : children) {
88 auto cref = ::fbl::MakeRefPtrUpgradeFromRaw(&craw, get_lock());
89 if (!cref)
90 continue;
91
92 *result = func(cref);
93 // |cref| might be the last reference at this point. If so,
94 // when we drop it in the next iteration the object dtor
95 // would be called here with the |get_lock()| held. To avoid that
96 // we keep the reference alive in the |refs| array and pass
97 // the responsibility of releasing them outside the lock to
98 // the caller.
99 refs[ix++] = ktl::move(cref);
100
101 if (*result != ZX_OK)
102 break;
103 }
104
105 return refs;
106 }
107
CreateRootJob()108 fbl::RefPtr<JobDispatcher> JobDispatcher::CreateRootJob() {
109 fbl::AllocChecker ac;
110 auto job = fbl::AdoptRef(new (&ac) JobDispatcher(0u, nullptr, JobPolicy()));
111 if (!ac.check())
112 return nullptr;
113 job->set_name(kRootJobName, sizeof(kRootJobName));
114 return job;
115 }
116
Create(uint32_t flags,fbl::RefPtr<JobDispatcher> parent,fbl::RefPtr<Dispatcher> * dispatcher,zx_rights_t * rights)117 zx_status_t JobDispatcher::Create(uint32_t flags,
118 fbl::RefPtr<JobDispatcher> parent,
119 fbl::RefPtr<Dispatcher>* dispatcher,
120 zx_rights_t* rights) {
121 if (parent != nullptr && parent->max_height() == 0) {
122 // The parent job cannot have children.
123 return ZX_ERR_OUT_OF_RANGE;
124 }
125
126 fbl::AllocChecker ac;
127 fbl::RefPtr<JobDispatcher> job =
128 fbl::AdoptRef(new (&ac) JobDispatcher(flags, parent, parent->GetPolicy()));
129 if (!ac.check())
130 return ZX_ERR_NO_MEMORY;
131
132 if (!parent->AddChildJob(job)) {
133 return ZX_ERR_BAD_STATE;
134 }
135
136 *rights = default_rights();
137 *dispatcher = ktl::move(job);
138 return ZX_OK;
139 }
140
JobDispatcher(uint32_t,fbl::RefPtr<JobDispatcher> parent,JobPolicy policy)141 JobDispatcher::JobDispatcher(uint32_t /*flags*/,
142 fbl::RefPtr<JobDispatcher> parent,
143 JobPolicy policy)
144 : SoloDispatcher(ZX_JOB_NO_PROCESSES | ZX_JOB_NO_JOBS),
145 parent_(ktl::move(parent)),
146 max_height_(parent_ ? parent_->max_height() - 1 : kRootJobMaxHeight),
147 state_(State::READY),
148 process_count_(0u),
149 job_count_(0u),
150 kill_on_oom_(false),
151 policy_(policy) {
152
153 // Set the initial job order, and try to make older jobs closer to
154 // the root (both hierarchically and temporally) show up earlier
155 // in enumeration.
156 if (parent_ == nullptr) {
157 // Root job is the most important.
158 Guard<fbl::Mutex> guard{AllJobsLock::Get()};
159 all_jobs_list_.push_back(this);
160 } else {
161 Guard<fbl::Mutex> parent_guard{parent_->get_lock()};
162 JobDispatcher* neighbor;
163 if (!parent_->jobs_.is_empty()) {
164 // Our youngest sibling.
165 //
166 // IMPORTANT: We must hold the parent's lock during list insertion
167 // to ensure that our sibling stays alive until we're done with it.
168 // The sibling may be in its dtor right now, trying to remove itself
169 // from parent_->jobs_ but blocked on parent_->get_lock(), and could be
170 // freed if we released the lock.
171 neighbor = &parent_->jobs_.back();
172
173 // This can't be us: we aren't added to our parent's child list
174 // until after construction.
175 DEBUG_ASSERT(!dll_job_raw_.InContainer());
176 DEBUG_ASSERT(neighbor != this);
177 } else {
178 // Our parent.
179 neighbor = parent_.get();
180 }
181
182 // Make ourselves appear after our next-youngest neighbor.
183 Guard<fbl::Mutex> guard{AllJobsLock::Get()};
184 all_jobs_list_.insert(all_jobs_list_.make_iterator(*neighbor), this);
185 }
186 }
187
~JobDispatcher()188 JobDispatcher::~JobDispatcher() {
189 if (parent_)
190 parent_->RemoveChildJob(this);
191
192 {
193 Guard<fbl::Mutex> guard{AllJobsLock::Get()};
194 DEBUG_ASSERT(dll_all_jobs_.InContainer());
195 all_jobs_list_.erase(*this);
196 }
197 }
198
get_related_koid() const199 zx_koid_t JobDispatcher::get_related_koid() const {
200 return parent_ ? parent_->get_koid() : 0u;
201 }
202
AddChildProcess(const fbl::RefPtr<ProcessDispatcher> & process)203 bool JobDispatcher::AddChildProcess(const fbl::RefPtr<ProcessDispatcher>& process) {
204 canary_.Assert();
205
206 Guard<fbl::Mutex> guard{get_lock()};
207 if (state_ != State::READY)
208 return false;
209 procs_.push_back(process.get());
210 ++process_count_;
211 UpdateSignalsIncrementLocked();
212 return true;
213 }
214
AddChildJob(const fbl::RefPtr<JobDispatcher> & job)215 bool JobDispatcher::AddChildJob(const fbl::RefPtr<JobDispatcher>& job) {
216 canary_.Assert();
217
218 Guard<fbl::Mutex> guard{get_lock()};
219 if (state_ != State::READY)
220 return false;
221
222 jobs_.push_back(job.get());
223 ++job_count_;
224 UpdateSignalsIncrementLocked();
225 return true;
226 }
227
RemoveChildProcess(ProcessDispatcher * process)228 void JobDispatcher::RemoveChildProcess(ProcessDispatcher* process) {
229 canary_.Assert();
230
231 Guard<fbl::Mutex> guard{get_lock()};
232 // The process dispatcher can call us in its destructor, Kill(),
233 // or RemoveThread().
234 if (!ProcessDispatcher::JobListTraitsRaw::node_state(*process).InContainer())
235 return;
236 procs_.erase(*process);
237 --process_count_;
238 UpdateSignalsDecrementLocked();
239 }
240
RemoveChildJob(JobDispatcher * job)241 void JobDispatcher::RemoveChildJob(JobDispatcher* job) {
242 canary_.Assert();
243
244 Guard<fbl::Mutex> guard{get_lock()};
245 if (!JobDispatcher::ListTraitsRaw::node_state(*job).InContainer())
246 return;
247 jobs_.erase(*job);
248 --job_count_;
249 UpdateSignalsDecrementLocked();
250 }
251
UpdateSignalsDecrementLocked()252 void JobDispatcher::UpdateSignalsDecrementLocked() {
253 canary_.Assert();
254
255 DEBUG_ASSERT(get_lock()->lock().IsHeld());
256
257 // removing jobs or processes.
258 zx_signals_t set = 0u;
259 if (process_count_ == 0u) {
260 DEBUG_ASSERT(procs_.is_empty());
261 set |= ZX_JOB_NO_PROCESSES;
262 }
263 if (job_count_ == 0u) {
264 DEBUG_ASSERT(jobs_.is_empty());
265 set |= ZX_JOB_NO_JOBS;
266 }
267
268 if ((job_count_ == 0) && (process_count_ == 0)) {
269 if (state_ == State::KILLING)
270 state_ = State::DEAD;
271
272 if (!parent_) {
273 // There are no userspace process left. From here, there's
274 // no particular context as to whether this was
275 // intentional, or if a core devhost crashed due to a
276 // bug. Either way, shut down the kernel.
277 platform_halt(HALT_ACTION_HALT, HALT_REASON_SW_RESET);
278 }
279 }
280
281 UpdateStateLocked(0u, set);
282 }
283
UpdateSignalsIncrementLocked()284 void JobDispatcher::UpdateSignalsIncrementLocked() {
285 canary_.Assert();
286
287 DEBUG_ASSERT(get_lock()->lock().IsHeld());
288
289 // Adding jobs or processes.
290 zx_signals_t clear = 0u;
291 if (process_count_ == 1u) {
292 DEBUG_ASSERT(!procs_.is_empty());
293 clear |= ZX_JOB_NO_PROCESSES;
294 }
295 if (job_count_ == 1u) {
296 DEBUG_ASSERT(!jobs_.is_empty());
297 clear |= ZX_JOB_NO_JOBS;
298 }
299 UpdateStateLocked(clear, 0u);
300 }
301
GetPolicy() const302 JobPolicy JobDispatcher::GetPolicy() const {
303 Guard<fbl::Mutex> guard{get_lock()};
304 return policy_;
305 }
306
Kill()307 bool JobDispatcher::Kill() {
308 canary_.Assert();
309
310 JobList jobs_to_kill;
311 ProcessList procs_to_kill;
312
313 LiveRefsArray jobs_refs;
314 LiveRefsArray proc_refs;
315
316 {
317 Guard<fbl::Mutex> guard{get_lock()};
318 if (state_ != State::READY)
319 return false;
320
321 state_ = State::KILLING;
322 zx_status_t result;
323
324 // Safely gather refs to the children.
325 jobs_refs = ForEachChildInLocked(jobs_, &result, [&](fbl::RefPtr<JobDispatcher> job) {
326 jobs_to_kill.push_front(ktl::move(job));
327 return ZX_OK;
328 });
329 proc_refs = ForEachChildInLocked(procs_, &result, [&](fbl::RefPtr<ProcessDispatcher> proc) {
330 procs_to_kill.push_front(ktl::move(proc));
331 return ZX_OK;
332 });
333 }
334
335 // Since we kill the child jobs first we have a depth-first massacre.
336 while (!jobs_to_kill.is_empty()) {
337 // TODO(cpu): This recursive call can overflow the stack.
338 jobs_to_kill.pop_front()->Kill();
339 }
340
341 while (!procs_to_kill.is_empty()) {
342 procs_to_kill.pop_front()->Kill();
343 }
344
345 return true;
346 }
347
SetBasicPolicy(uint32_t mode,const zx_policy_basic * in_policy,size_t policy_count)348 zx_status_t JobDispatcher::SetBasicPolicy(
349 uint32_t mode, const zx_policy_basic* in_policy, size_t policy_count) {
350
351 Guard<fbl::Mutex> guard{get_lock()};
352
353 // Can't set policy when there are active processes or jobs. This constraint ensures that a
354 // process's policy cannot change over its lifetime. Because a process's policy cannot change,
355 // the risk of TOCTOU bugs is reduced and we are free to apply policy at the ProcessDispatcher
356 // without having to walk up the tree to its containing job.
357 if (!procs_.is_empty() || !jobs_.is_empty())
358 return ZX_ERR_BAD_STATE;
359
360 auto status = policy_.AddBasicPolicy(mode, in_policy, policy_count);
361
362 if (status != ZX_OK)
363 return status;
364
365 return ZX_OK;
366 }
367
EnumerateChildren(JobEnumerator * je,bool recurse)368 bool JobDispatcher::EnumerateChildren(JobEnumerator* je, bool recurse) {
369 canary_.Assert();
370
371 LiveRefsArray jobs_refs;
372 LiveRefsArray proc_refs;
373
374 zx_status_t result = ZX_OK;
375
376 {
377 Guard<fbl::Mutex> guard{get_lock()};
378
379 proc_refs = ForEachChildInLocked(
380 procs_, &result, [&](fbl::RefPtr<ProcessDispatcher> proc) {
381 return je->OnProcess(proc.get()) ? ZX_OK : ZX_ERR_STOP;
382 });
383 if (result != ZX_OK) {
384 return false;
385 }
386
387 jobs_refs = ForEachChildInLocked(jobs_, &result, [&](fbl::RefPtr<JobDispatcher> job) {
388 if (!je->OnJob(job.get())) {
389 return ZX_ERR_STOP;
390 }
391 if (recurse) {
392 // TODO(kulakowski): This recursive call can overflow the stack.
393 return job->EnumerateChildren(je, /* recurse */ true)
394 ? ZX_OK
395 : ZX_ERR_STOP;
396 }
397 return ZX_OK;
398 });
399 }
400
401 return result == ZX_OK;
402 }
403
404 fbl::RefPtr<ProcessDispatcher>
LookupProcessById(zx_koid_t koid)405 JobDispatcher::LookupProcessById(zx_koid_t koid) {
406 canary_.Assert();
407
408 LiveRefsArray proc_refs;
409
410 fbl::RefPtr<ProcessDispatcher> found_proc;
411 {
412 Guard<fbl::Mutex> guard{get_lock()};
413 zx_status_t result;
414
415 proc_refs = ForEachChildInLocked(procs_, &result, [&](fbl::RefPtr<ProcessDispatcher> proc) {
416 if (proc->get_koid() == koid) {
417 found_proc = ktl::move(proc);
418 return ZX_ERR_STOP;
419 }
420 return ZX_OK;
421 });
422 }
423 return found_proc; // Null if not found.
424 }
425
426 fbl::RefPtr<JobDispatcher>
LookupJobById(zx_koid_t koid)427 JobDispatcher::LookupJobById(zx_koid_t koid) {
428 canary_.Assert();
429
430 LiveRefsArray jobs_refs;
431
432 fbl::RefPtr<JobDispatcher> found_job;
433 {
434 Guard<fbl::Mutex> guard{get_lock()};
435 zx_status_t result;
436
437 jobs_refs = ForEachChildInLocked(jobs_, &result, [&](fbl::RefPtr<JobDispatcher> job) {
438 if (job->get_koid() == koid) {
439 found_job = ktl::move(job);
440 return ZX_ERR_STOP;
441 }
442 return ZX_OK;
443 });
444 }
445 return found_job; // Null if not found.
446 }
447
get_name(char out_name[ZX_MAX_NAME_LEN]) const448 void JobDispatcher::get_name(char out_name[ZX_MAX_NAME_LEN]) const {
449 canary_.Assert();
450
451 name_.get(ZX_MAX_NAME_LEN, out_name);
452 }
453
set_name(const char * name,size_t len)454 zx_status_t JobDispatcher::set_name(const char* name, size_t len) {
455 canary_.Assert();
456
457 return name_.set(name, len);
458 }
459
460 // Global list of all jobs.
461 JobDispatcher::AllJobsList JobDispatcher::all_jobs_list_;
462
SetExceptionPort(fbl::RefPtr<ExceptionPort> eport)463 zx_status_t JobDispatcher::SetExceptionPort(fbl::RefPtr<ExceptionPort> eport) {
464 canary_.Assert();
465 bool debugger = false;
466 switch (eport->type()) {
467 case ExceptionPort::Type::JOB_DEBUGGER:
468 debugger = true;
469 break;
470 case ExceptionPort::Type::JOB:
471 break;
472 default:
473 DEBUG_ASSERT_MSG(false, "unexpected port type: %d",
474 static_cast<int>(eport->type()));
475 break;
476 }
477
478 Guard<fbl::Mutex> guard{get_lock()};
479 if (debugger) {
480 if (debugger_exception_port_)
481 return ZX_ERR_ALREADY_BOUND;
482 debugger_exception_port_ = ktl::move(eport);
483 } else {
484 if (exception_port_)
485 return ZX_ERR_ALREADY_BOUND;
486 exception_port_ = ktl::move(eport);
487 }
488 return ZX_OK;
489 }
490
491 class OnExceptionPortRemovalEnumerator final : public JobEnumerator {
492 public:
OnExceptionPortRemovalEnumerator(fbl::RefPtr<ExceptionPort> eport)493 OnExceptionPortRemovalEnumerator(fbl::RefPtr<ExceptionPort> eport)
494 : eport_(ktl::move(eport)) {}
495 OnExceptionPortRemovalEnumerator(const OnExceptionPortRemovalEnumerator&) = delete;
496
497 private:
OnProcess(ProcessDispatcher * process)498 bool OnProcess(ProcessDispatcher* process) override {
499 process->OnExceptionPortRemoval(eport_);
500 // Keep looking.
501 return true;
502 }
503
504 fbl::RefPtr<ExceptionPort> eport_;
505 };
506
ResetExceptionPort(bool debugger)507 bool JobDispatcher::ResetExceptionPort(bool debugger) {
508 canary_.Assert();
509
510 fbl::RefPtr<ExceptionPort> eport;
511 {
512 Guard<fbl::Mutex> lock{get_lock()};
513 if (debugger) {
514 debugger_exception_port_.swap(eport);
515 } else {
516 exception_port_.swap(eport);
517 }
518 if (eport == nullptr) {
519 // Attempted to unbind when no exception port is bound.
520 return false;
521 }
522 // This method must guarantee that no caller will return until
523 // OnTargetUnbind has been called on the port-to-unbind.
524 // This becomes important when a manual unbind races with a
525 // PortDispatcher::on_zero_handles auto-unbind.
526 //
527 // If OnTargetUnbind were called outside of the lock, it would lead to
528 // a race (for threads A and B):
529 //
530 // A: Calls ResetExceptionPort; acquires the lock
531 // A: Sees a non-null exception_port_, swaps it into the eport local.
532 // exception_port_ is now null.
533 // A: Releases the lock
534 //
535 // B: Calls ResetExceptionPort; acquires the lock
536 // B: Sees a null exception_port_ and returns. But OnTargetUnbind()
537 // hasn't yet been called for the port.
538 //
539 // So, call it before releasing the lock.
540 eport->OnTargetUnbind();
541 }
542
543 OnExceptionPortRemovalEnumerator remover(eport);
544 if (!EnumerateChildren(&remover, true)) {
545 DEBUG_ASSERT(false);
546 }
547 return true;
548 }
549
exception_port()550 fbl::RefPtr<ExceptionPort> JobDispatcher::exception_port() {
551 Guard<fbl::Mutex> lock{get_lock()};
552 return exception_port_;
553 }
554
debugger_exception_port()555 fbl::RefPtr<ExceptionPort> JobDispatcher::debugger_exception_port() {
556 Guard<fbl::Mutex> guard{get_lock()};
557 return debugger_exception_port_;
558 }
559
set_kill_on_oom(bool value)560 void JobDispatcher::set_kill_on_oom(bool value) {
561 Guard<fbl::Mutex> guard{get_lock()};
562 kill_on_oom_ = value;
563 };
564
get_kill_on_oom() const565 bool JobDispatcher::get_kill_on_oom() const {
566 Guard<fbl::Mutex> guard{get_lock()};
567 return kill_on_oom_;
568 }
569