| // Copyright 2018 The Crashpad Authors. All rights reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "util/fuchsia/koid_utilities.h" |
| |
| #include <fuchsia/sysinfo/c/fidl.h> |
| #include <lib/fdio/fdio.h> |
| #include <lib/zx/channel.h> |
| #include <lib/zx/job.h> |
| #include <lib/zx/process.h> |
| |
| #include <vector> |
| |
| #include "base/files/file_path.h" |
| #include "base/fuchsia/fuchsia_logging.h" |
| #include "util/file/file_io.h" |
| |
| namespace crashpad { |
| |
| namespace { |
| |
| // Casts |handle| into a container of type T, returning a null handle if the |
| // actual handle type does not match that of T. |
| template <typename T> |
| T CastHandle(zx::handle handle) { |
| zx_info_handle_basic_t actual = {}; |
| zx_status_t status = handle.get_info( |
| ZX_INFO_HANDLE_BASIC, &actual, sizeof(actual), nullptr, nullptr); |
| if (status != ZX_OK) { |
| ZX_LOG(ERROR, status) << "zx_object_get_info"; |
| return T(); |
| } |
| if (actual.type != T::TYPE) { |
| LOG(ERROR) << "Wrong type: " << actual.type << ", expected " << T::TYPE; |
| return T(); |
| } |
| return T(std::move(handle)); |
| } |
| |
| // Returns null handle if |koid| is not found or an error occurs. If |was_found| |
| // is non-null then it will be set, to distinguish not-found from other errors. |
| template <typename T, typename U> |
| T GetChildHandleByKoid(const U& parent, zx_koid_t child_koid, bool* was_found) { |
| zx::handle handle; |
| zx_status_t status = |
| parent.get_child(child_koid, ZX_RIGHT_SAME_RIGHTS, &handle); |
| if (was_found) |
| *was_found = (status != ZX_ERR_NOT_FOUND); |
| if (status != ZX_OK) { |
| ZX_LOG(ERROR, status) << "zx_object_get_child"; |
| return T(); |
| } |
| |
| return CastHandle<T>(std::move(handle)); |
| } |
| |
| } // namespace |
| |
| std::vector<zx_koid_t> GetChildKoids(const zx::object_base& parent_object, |
| zx_object_info_topic_t child_kind) { |
| size_t actual = 0; |
| size_t available = 0; |
| std::vector<zx_koid_t> result(100); |
| zx::unowned_handle parent(parent_object.get()); |
| |
| // This is inherently racy. Better if the process is suspended, but there's |
| // still no guarantee that a thread isn't externally created. As a result, |
| // must be in a retry loop. |
| for (;;) { |
| zx_status_t status = parent->get_info(child_kind, |
| result.data(), |
| result.size() * sizeof(zx_koid_t), |
| &actual, |
| &available); |
| // If the buffer is too small (even zero), the result is still ZX_OK, not |
| // ZX_ERR_BUFFER_TOO_SMALL. |
| if (status != ZX_OK) { |
| ZX_LOG(ERROR, status) << "zx_object_get_info"; |
| break; |
| } |
| |
| if (actual == available) { |
| break; |
| } |
| |
| // Resize to the expected number next time, with a bit of slop to handle the |
| // race between here and the next request. |
| result.resize(available + 10); |
| } |
| |
| result.resize(actual); |
| return result; |
| } |
| |
| std::vector<zx::thread> GetThreadHandles(const zx::process& parent) { |
| auto koids = GetChildKoids(parent, ZX_INFO_PROCESS_THREADS); |
| return GetHandlesForThreadKoids(parent, koids); |
| } |
| |
| std::vector<zx::thread> GetHandlesForThreadKoids( |
| const zx::process& parent, |
| const std::vector<zx_koid_t>& koids) { |
| std::vector<zx::thread> result; |
| result.reserve(koids.size()); |
| for (zx_koid_t koid : koids) { |
| result.emplace_back(GetThreadHandleByKoid(parent, koid)); |
| } |
| return result; |
| } |
| |
| zx::thread GetThreadHandleByKoid(const zx::process& parent, |
| zx_koid_t child_koid) { |
| return GetChildHandleByKoid<zx::thread>(parent, child_koid, nullptr); |
| } |
| |
| zx_koid_t GetKoidForHandle(const zx::object_base& object) { |
| zx_info_handle_basic_t info; |
| zx_status_t status = zx_object_get_info(object.get(), |
| ZX_INFO_HANDLE_BASIC, |
| &info, |
| sizeof(info), |
| nullptr, |
| nullptr); |
| if (status != ZX_OK) { |
| ZX_LOG(ERROR, status) << "zx_object_get_info"; |
| return ZX_KOID_INVALID; |
| } |
| return info.koid; |
| } |
| |
| } // namespace crashpad |