1 // Copyright 2017 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 "crash-list.h"
6 
7 #include <stdlib.h>
8 #include <threads.h>
9 
10 #include <unittest/unittest.h>
11 #include <zircon/listnode.h>
12 #include <zircon/status.h>
13 #include <zircon/syscalls/object.h>
14 
15 // Details of process or thread registered as expected to crash.
16 struct crash_proc_t {
17     zx_handle_t handle;
18     zx_koid_t koid;
19     // Node stored in crash handler list.
20     list_node_t node;
21 };
22 
23 struct crash_list {
24     // The list may be accessed by the main test thread and crash handler thread.
25     mtx_t mutex;
26     // Head of list containing crash_proc_t.
27     list_node_t should_crash_procs;
28 };
29 
crash_list_new()30 crash_list_t crash_list_new() {
31     crash_list_t crash_list = static_cast<crash_list_t>(malloc(sizeof(struct crash_list)));
32     if (crash_list == nullptr) {
33         UNITTEST_FAIL_TRACEF("FATAL: could not malloc crash list\n");
34         exit(ZX_ERR_INTERNAL);
35     }
36     int ret = mtx_init(&crash_list->mutex, mtx_plain);
37     if (ret != thrd_success) {
38         UNITTEST_FAIL_TRACEF("FATAL: could not create crash list mutex : error %s\n",
39                              zx_status_get_string(ret));
40         exit(ZX_ERR_INTERNAL);
41     }
42     crash_list->should_crash_procs =
43         (list_node_t)LIST_INITIAL_VALUE(crash_list->should_crash_procs);
44     return crash_list;
45 }
46 
crash_list_register(crash_list_t crash_list,zx_handle_t handle)47 void crash_list_register(crash_list_t crash_list, zx_handle_t handle) {
48     if (crash_list == nullptr) {
49         UNITTEST_FAIL_TRACEF(
50             "FATAL: crash list was NULL, run test with RUN_TEST_ENABLE_CRASH_HANDLER\n");
51         exit(ZX_ERR_INTERNAL);
52     }
53     zx_info_handle_basic_t info;
54     zx_status_t status =
55         zx_object_get_info(handle, ZX_INFO_HANDLE_BASIC, &info, sizeof(info), nullptr, nullptr);
56     if (status != ZX_OK) {
57         UNITTEST_FAIL_TRACEF("FATAL: could not get handle info : error %s\n",
58                              zx_status_get_string(status));
59         exit(ZX_ERR_INTERNAL);
60     }
61     zx_handle_t copy;
62     status = zx_handle_duplicate(handle, ZX_RIGHT_SAME_RIGHTS, &copy);
63     if (status != ZX_OK) {
64         UNITTEST_FAIL_TRACEF("FATAL: could not duplicate handle : error %s\n",
65                              zx_status_get_string(status));
66         exit(ZX_ERR_INTERNAL);
67     }
68     crash_proc_t* crash_proc = static_cast<crash_proc_t*>(malloc(sizeof(crash_proc_t)));
69     if (crash_list == nullptr) {
70         UNITTEST_FAIL_TRACEF("FATAL: could not malloc crash proc\n");
71         exit(ZX_ERR_INTERNAL);
72     }
73     crash_proc->handle = copy;
74     crash_proc->koid = info.koid;
75 
76     mtx_lock(&crash_list->mutex);
77     list_add_head(&crash_list->should_crash_procs, &crash_proc->node);
78     mtx_unlock(&crash_list->mutex);
79 }
80 
crash_list_lookup_koid(crash_list_t crash_list,zx_koid_t koid)81 zx_handle_t crash_list_lookup_koid(crash_list_t crash_list, zx_koid_t koid) {
82     if (crash_list == nullptr) {
83         UNITTEST_FAIL_TRACEF(
84             "FATAL: crash list was NULL, run test with RUN_TEST_ENABLE_CRASH_HANDLER\n");
85         exit(ZX_ERR_INTERNAL);
86     }
87     zx_handle_t proc = ZX_HANDLE_INVALID;
88     crash_proc_t* cur = nullptr;
89 
90     mtx_lock(&crash_list->mutex);
91     list_for_every_entry (&crash_list->should_crash_procs, cur, crash_proc_t, node) {
92         if (cur->koid == koid) {
93             proc = cur->handle;
94             break;
95         }
96     }
97     mtx_unlock(&crash_list->mutex);
98     return proc;
99 }
100 
crash_list_delete_koid(crash_list_t crash_list,zx_koid_t koid)101 zx_handle_t crash_list_delete_koid(crash_list_t crash_list, zx_koid_t koid) {
102     if (crash_list == nullptr) {
103         UNITTEST_FAIL_TRACEF(
104             "FATAL: crash list was NULL, run test with RUN_TEST_ENABLE_CRASH_HANDLER\n");
105         exit(ZX_ERR_INTERNAL);
106     }
107     zx_handle_t deleted_proc = ZX_HANDLE_INVALID;
108     crash_proc_t* cur = nullptr;
109     crash_proc_t* tmp = nullptr;
110 
111     mtx_lock(&crash_list->mutex);
112     list_for_every_entry_safe (&crash_list->should_crash_procs, cur, tmp, crash_proc_t, node) {
113         if (cur->koid == koid) {
114             deleted_proc = cur->handle;
115             list_delete(&cur->node);
116             free(cur);
117             break;
118         }
119     }
120     mtx_unlock(&crash_list->mutex);
121     return deleted_proc;
122 }
123 
crash_list_delete(crash_list_t crash_list)124 bool crash_list_delete(crash_list_t crash_list) {
125     if (crash_list == nullptr) {
126         UNITTEST_FAIL_TRACEF(
127             "FATAL: crash list was NULL, run test with RUN_TEST_ENABLE_CRASH_HANDLER\n");
128         exit(ZX_ERR_INTERNAL);
129     }
130     crash_proc_t* cur = nullptr;
131     crash_proc_t* tmp = nullptr;
132 
133     bool deleted = false;
134     list_for_every_entry_safe (&crash_list->should_crash_procs, cur, tmp, crash_proc_t, node) {
135         zx_handle_close(cur->handle);
136         deleted = true;
137         list_delete(&cur->node);
138         free(cur);
139     }
140     mtx_destroy(&crash_list->mutex);
141     free(crash_list);
142     return deleted;
143 }
144