1 // Copyright 2018 The Crashpad Authors. All rights reserved.
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 //     http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14 
15 #include "util/fuchsia/koid_utilities.h"
16 
17 #include <lib/fdio/fdio.h>
18 #include <lib/zx/channel.h>
19 #include <lib/zx/job.h>
20 #include <lib/zx/process.h>
21 
22 #include <vector>
23 
24 #include "base/files/file_path.h"
25 #include "base/fuchsia/fuchsia_logging.h"
26 #include "util/file/file_io.h"
27 
28 namespace crashpad {
29 
30 namespace {
31 
32 // Casts |handle| into a container of type T, returning a null handle if the
33 // actual handle type does not match that of T.
34 template <typename T>
CastHandle(zx::handle handle)35 T CastHandle(zx::handle handle) {
36   zx_info_handle_basic_t actual = {};
37   zx_status_t status = handle.get_info(
38       ZX_INFO_HANDLE_BASIC, &actual, sizeof(actual), nullptr, nullptr);
39   if (status != ZX_OK) {
40     ZX_LOG(ERROR, status) << "zx_object_get_info";
41     return T();
42   }
43   if (actual.type != T::TYPE) {
44     LOG(ERROR) << "Wrong type: " << actual.type << ", expected " << T::TYPE;
45     return T();
46   }
47   return T(std::move(handle));
48 }
49 
50 // Returns null handle if |koid| is not found or an error occurs. If |was_found|
51 // is non-null then it will be set, to distinguish not-found from other errors.
52 template <typename T, typename U>
53 T GetChildHandleByKoid(const U& parent, zx_koid_t child_koid, bool* was_found) {
54   zx::handle handle;
55   zx_status_t status =
56       parent.get_child(child_koid, ZX_RIGHT_SAME_RIGHTS, &handle);
57   if (was_found)
58     *was_found = (status != ZX_ERR_NOT_FOUND);
59   if (status != ZX_OK) {
60     ZX_LOG(ERROR, status) << "zx_object_get_child";
61     return T();
62   }
63 
64   return CastHandle<T>(std::move(handle));
65 }
66 
67 }  // namespace
68 
GetChildKoids(const zx::object_base & parent_object,zx_object_info_topic_t child_kind)69 std::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent_object,
70                                      zx_object_info_topic_t child_kind) {
71   size_t actual = 0;
72   size_t available = 0;
73   std::vector<zx_koid_t> result(100);
74   zx::unowned_handle parent(parent_object.get());
75 
76   // This is inherently racy. Better if the process is suspended, but there's
77   // still no guarantee that a thread isn't externally created. As a result,
78   // must be in a retry loop.
79   for (;;) {
80     zx_status_t status = parent->get_info(child_kind,
81                                           result.data(),
82                                           result.size() * sizeof(zx_koid_t),
83                                           &actual,
84                                           &available);
85     // If the buffer is too small (even zero), the result is still ZX_OK, not
86     // ZX_ERR_BUFFER_TOO_SMALL.
87     if (status != ZX_OK) {
88       ZX_LOG(ERROR, status) << "zx_object_get_info";
89       break;
90     }
91 
92     if (actual == available) {
93       break;
94     }
95 
96     // Resize to the expected number next time, with a bit of slop to handle the
97     // race between here and the next request.
98     result.resize(available + 10);
99   }
100 
101   result.resize(actual);
102   return result;
103 }
104 
GetThreadHandles(const zx::process & parent)105 std::vector<zx::thread> GetThreadHandles(const zx::process& parent) {
106   auto koids = GetChildKoids(parent, ZX_INFO_PROCESS_THREADS);
107   return GetHandlesForThreadKoids(parent, koids);
108 }
109 
GetHandlesForThreadKoids(const zx::process & parent,const std::vector<zx_koid_t> & koids)110 std::vector<zx::thread> GetHandlesForThreadKoids(
111     const zx::process& parent,
112     const std::vector<zx_koid_t>& koids) {
113   std::vector<zx::thread> result;
114   result.reserve(koids.size());
115   for (zx_koid_t koid : koids) {
116     result.emplace_back(GetThreadHandleByKoid(parent, koid));
117   }
118   return result;
119 }
120 
GetThreadHandleByKoid(const zx::process & parent,zx_koid_t child_koid)121 zx::thread GetThreadHandleByKoid(const zx::process& parent,
122                                  zx_koid_t child_koid) {
123   return GetChildHandleByKoid<zx::thread>(parent, child_koid, nullptr);
124 }
125 
GetKoidForHandle(const zx::object_base & object)126 zx_koid_t GetKoidForHandle(const zx::object_base& object) {
127   zx_info_handle_basic_t info;
128   zx_status_t status = zx_object_get_info(object.get(),
129                                           ZX_INFO_HANDLE_BASIC,
130                                           &info,
131                                           sizeof(info),
132                                           nullptr,
133                                           nullptr);
134   if (status != ZX_OK) {
135     ZX_LOG(ERROR, status) << "zx_object_get_info";
136     return ZX_KOID_INVALID;
137   }
138   return info.koid;
139 }
140 
141 }  // namespace crashpad
142