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.
16struct 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
23struct 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
30crash_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
47void 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
81zx_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
101zx_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
124bool 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