// Copyright 2018 The Crashpad Authors
//
// 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 <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