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