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 <arch.h>
8 #include <arch/exception.h>
9 #include <assert.h>
10 #include <err.h>
11 #include <inttypes.h>
12 #include <stdio.h>
13 #include <trace.h>
14 
15 #include <object/excp_port.h>
16 #include <object/job_dispatcher.h>
17 #include <object/process_dispatcher.h>
18 #include <object/thread_dispatcher.h>
19 
20 #define LOCAL_TRACE 0
21 #define TRACE_EXCEPTIONS 1
22 
excp_type_to_string(uint type)23 static const char* excp_type_to_string(uint type) {
24     switch (type) {
25     case ZX_EXCP_FATAL_PAGE_FAULT:
26         return "fatal page fault";
27     case ZX_EXCP_UNDEFINED_INSTRUCTION:
28         return "undefined instruction";
29     case ZX_EXCP_GENERAL:
30         return "general fault";
31     case ZX_EXCP_SW_BREAKPOINT:
32         return "software breakpoint";
33     case ZX_EXCP_HW_BREAKPOINT:
34         return "hardware breakpoint";
35     case ZX_EXCP_UNALIGNED_ACCESS:
36         return "alignment fault";
37     case ZX_EXCP_POLICY_ERROR:
38         return "policy error";
39     default:
40         return "unknown fault";
41     }
42 }
43 
44 // This isn't an "iterator" in the pure c++ sense. We don't need all that
45 // complexity. I just couldn't think of a better term.
46 //
47 // Exception ports are tried in the following order:
48 // - debugger
49 // - thread
50 // - process
51 // - job (first owning job, then its parent job, and so on up to root job)
52 // - system
53 class ExceptionPortIterator final {
54 public:
ExceptionPortIterator(ThreadDispatcher * thread)55     explicit ExceptionPortIterator(ThreadDispatcher* thread)
56       : thread_(thread),
57         previous_type_(ExceptionPort::Type::NONE) {
58     }
59 
60     // Returns true with |out_eport| filled in for the next one to try.
61     // Returns false if there are no more to try.
Next(fbl::RefPtr<ExceptionPort> * out_eport)62     bool Next(fbl::RefPtr<ExceptionPort>* out_eport) {
63         fbl::RefPtr<ExceptionPort> eport;
64         ExceptionPort::Type expected_type = ExceptionPort::Type::NONE;
65 
66         while (true) {
67             switch (previous_type_) {
68                 case ExceptionPort::Type::NONE:
69                     eport = thread_->process()->debugger_exception_port();
70                     expected_type = ExceptionPort::Type::DEBUGGER;
71                     break;
72                 case ExceptionPort::Type::DEBUGGER:
73                     eport = thread_->exception_port();
74                     expected_type = ExceptionPort::Type::THREAD;
75                     break;
76                 case ExceptionPort::Type::THREAD:
77                     eport = thread_->process()->exception_port();
78                     expected_type = ExceptionPort::Type::PROCESS;
79                     break;
80                 case ExceptionPort::Type::PROCESS:
81                     previous_job_ = thread_->process()->job();
82                     eport = previous_job_->exception_port();
83                     expected_type = ExceptionPort::Type::JOB;
84                     break;
85                 case ExceptionPort::Type::JOB:
86                     previous_job_ = previous_job_->parent();
87                     if (previous_job_) {
88                         eport = previous_job_->exception_port();
89                         expected_type = ExceptionPort::Type::JOB;
90                     } else {
91                         // Reached the root job and there was no handler.
92                        return false;
93                     }
94                     break;
95                 default:
96                     ASSERT_MSG(0, "unexpected exception type %d",
97                                static_cast<int>(previous_type_));
98                     __UNREACHABLE;
99             }
100             previous_type_ = expected_type;
101             if (eport) {
102                 DEBUG_ASSERT(eport->type() == expected_type);
103                 *out_eport = ktl::move(eport);
104                 return true;
105             }
106         }
107         __UNREACHABLE;
108     }
109 
110 private:
111     ThreadDispatcher* thread_;
112     ExceptionPort::Type previous_type_;
113     // Jobs are traversed up their hierarchy. This is the previous one.
114     fbl::RefPtr<JobDispatcher> previous_job_;
115 
116     DISALLOW_COPY_ASSIGN_AND_MOVE(ExceptionPortIterator);
117 };
118 
try_exception_handler(fbl::RefPtr<ExceptionPort> eport,ThreadDispatcher * thread,const zx_exception_report_t * report,const arch_exception_context_t * arch_context,ThreadState::Exception * estatus)119 static zx_status_t try_exception_handler(fbl::RefPtr<ExceptionPort> eport,
120                                          ThreadDispatcher* thread,
121                                          const zx_exception_report_t* report,
122                                          const arch_exception_context_t* arch_context,
123                                          ThreadState::Exception* estatus) {
124     LTRACEF("Trying exception port type %d\n", static_cast<int>(eport->type()));
125     auto status = thread->ExceptionHandlerExchange(eport, report, arch_context, estatus);
126     LTRACEF("ExceptionHandlerExchange returned status %d, estatus %d\n", status, static_cast<int>(*estatus));
127 
128     return status;
129 }
130 
131 enum handler_status_t {
132     // thread is to be resumed
133     HS_RESUME,
134     // thread was killed
135     HS_KILLED,
136     // exception not handled (process will be killed)
137     HS_NOT_HANDLED
138 };
139 
140 // Subroutine of dispatch_user_exception to simplify the code.
141 // One useful thing this does is guarantee ExceptionPortIterator is properly
142 // destructed.
143 // |*out_processed| is set to a boolean indicating if at least one
144 // handler processed the exception.
145 
exception_handler_worker(uint exception_type,arch_exception_context_t * context,ThreadDispatcher * thread,bool * out_processed)146 static handler_status_t exception_handler_worker(uint exception_type,
147                                                  arch_exception_context_t* context,
148                                                  ThreadDispatcher* thread,
149                                                  bool* out_processed) {
150     *out_processed = false;
151 
152     zx_exception_report_t report;
153     ExceptionPort::BuildArchReport(&report, exception_type, context);
154 
155     ExceptionPortIterator iter(thread);
156     fbl::RefPtr<ExceptionPort> eport;
157 
158     while (iter.Next(&eport)) {
159         // Initialize for paranoia's sake.
160         ThreadState::Exception estatus = ThreadState::Exception::UNPROCESSED;
161         auto status = try_exception_handler(eport, thread, &report, context, &estatus);
162         LTRACEF("handler returned %d/%d\n",
163                 static_cast<int>(status), static_cast<int>(estatus));
164         switch (status) {
165         case ZX_ERR_INTERNAL_INTR_KILLED:
166             // thread was killed, probably with zx_task_kill
167             return HS_KILLED;
168         case ZX_OK:
169             switch (estatus) {
170             case ThreadState::Exception::TRY_NEXT:
171                 *out_processed = true;
172                 break;
173             case ThreadState::Exception::RESUME:
174                 return HS_RESUME;
175             default:
176                 ASSERT_MSG(0, "invalid exception status %d",
177                            static_cast<int>(estatus));
178                 __UNREACHABLE;
179             }
180             break;
181         default:
182             // Instead of requiring exception processing to only return
183             // specific kinds of errors (and thus requiring us to be updated
184             // every time a change causes a new error to be returned), treat
185             // all other errors as fatal. It's debatable whether to give the
186             // next handler a try or immediately kill the task. By immediately
187             // killing the task we bypass the root job exception handler,
188             // but it feels safer.
189             // TODO(ZX-2853): Are there times when we should try harder to
190             // process the exception?
191             // Print something to give the user a clue.
192             printf("KERN: Error %d processing exception in user thread %lu.%lu\n",
193                    status, thread->process()->get_koid(), thread->get_koid());
194             // Still mark the exception as processed so that we don't trigger
195             // later bare-bones crash reporting (TRACE_EXCEPTIONS).
196             *out_processed = true;
197             return HS_NOT_HANDLED;
198         }
199     }
200 
201     return HS_NOT_HANDLED;
202 }
203 
204 // Dispatches an exception to the appropriate handler. Called by arch code
205 // when it cannot handle an exception.
206 //
207 // If we return ZX_OK, the caller is expected to resume the thread "as if"
208 // nothing happened, the handler is expected to have modified state such that
209 // resumption is possible.
210 //
211 // If we return ZX_ERR_BAD_STATE, the current thread is not a user thread
212 // (i.e., not associated with a ThreadDispatcher).
213 //
214 // Otherwise, we cause the current thread to exit and do not return at all.
215 //
216 // TODO(dje): Support unwinding from this exception and introducing a different
217 // exception?
dispatch_user_exception(uint exception_type,arch_exception_context_t * context)218 zx_status_t dispatch_user_exception(uint exception_type,
219                                     arch_exception_context_t* context) {
220     LTRACEF("type %u, context %p\n", exception_type, context);
221 
222     ThreadDispatcher* thread = ThreadDispatcher::GetCurrent();
223     if (unlikely(!thread)) {
224         // The current thread is not a user thread; bail.
225         return ZX_ERR_BAD_STATE;
226     }
227 
228     // From now until the exception is resolved the thread is in an exception.
229     ThreadDispatcher::AutoBlocked by(ThreadDispatcher::Blocked::EXCEPTION);
230 
231     bool processed;
232     handler_status_t hstatus = exception_handler_worker(exception_type, context,
233                                                         thread, &processed);
234     switch (hstatus) {
235         case HS_RESUME:
236             return ZX_OK;
237         case HS_KILLED:
238             thread->Exit();
239             __UNREACHABLE;
240         case HS_NOT_HANDLED:
241             break;
242         default:
243             ASSERT_MSG(0, "unexpected exception worker result %d", static_cast<int>(hstatus));
244             __UNREACHABLE;
245     }
246 
247     auto process = thread->process();
248 
249 #if TRACE_EXCEPTIONS
250     if (!processed) {
251         // only print this if an exception handler wasn't involved
252         // in handling the exception
253         char pname[ZX_MAX_NAME_LEN];
254         process->get_name(pname);
255         char tname[ZX_MAX_NAME_LEN];
256         thread->get_name(tname);
257         printf("KERN: %s in user thread '%s' in process '%s'\n",
258                excp_type_to_string(exception_type), tname, pname);
259 
260         arch_dump_exception_context(context);
261     }
262 #endif
263 
264     // kill our process
265     process->Kill();
266 
267     // exit
268     thread->Exit();
269 
270     // should not get here
271     panic("fell out of thread exit somehow!\n");
272     __UNREACHABLE;
273 }
274