| // Copyright 2019 The Fuchsia Authors |
| // |
| // Use of this source code is governed by a MIT-style |
| // license that can be found in the LICENSE file or at |
| // https://opensource.org/licenses/MIT |
| |
| #include "object/user_handles.h" |
| |
| #include <ktl/algorithm.h> |
| |
| #include <ktl/enforce.h> |
| |
| namespace { |
| // Basic checks for a |handle| to be able to be sent via |channel|. |
| static zx_status_t handle_checks_locked(const Handle* handle, const Dispatcher* channel, |
| zx_handle_op_t operation, zx_rights_t desired_rights, |
| zx_obj_type_t type) { |
| if (!handle) |
| return ZX_ERR_BAD_HANDLE; |
| if (!handle->HasRights(ZX_RIGHT_TRANSFER)) |
| return ZX_ERR_ACCESS_DENIED; |
| if (handle->dispatcher().get() == channel) |
| return ZX_ERR_NOT_SUPPORTED; |
| if (type != ZX_OBJ_TYPE_NONE && handle->dispatcher()->get_type() != type) |
| return ZX_ERR_WRONG_TYPE; |
| if (operation != ZX_HANDLE_OP_MOVE && operation != ZX_HANDLE_OP_DUPLICATE) |
| return ZX_ERR_INVALID_ARGS; |
| if (desired_rights != ZX_RIGHT_SAME_RIGHTS) { |
| if ((handle->rights() & desired_rights) != desired_rights) { |
| return ZX_ERR_INVALID_ARGS; |
| } |
| } |
| if ((operation == ZX_HANDLE_OP_DUPLICATE) && !handle->HasRights(ZX_RIGHT_DUPLICATE)) |
| return ZX_ERR_ACCESS_DENIED; |
| return ZX_OK; |
| } |
| |
| } // namespace |
| |
| zx_status_t get_user_handles_to_consume(user_in_ptr<const zx_handle_t> user_handles, size_t offset, |
| size_t chunk_size, zx_handle_t* handles) { |
| return user_handles.copy_array_from_user(handles, chunk_size, offset); |
| } |
| |
| zx_status_t get_user_handles_to_consume(user_inout_ptr<zx_handle_disposition_t> user_handles, |
| size_t offset, size_t chunk_size, zx_handle_t* handles) { |
| zx_handle_disposition_t local_handle_disposition[kMaxMessageHandles] = {}; |
| |
| chunk_size = ktl::min<size_t>(chunk_size, kMaxMessageHandles); |
| |
| zx_status_t status = |
| user_handles.copy_array_from_user(local_handle_disposition, chunk_size, offset); |
| if (status != ZX_OK) { |
| return status; |
| } |
| |
| for (size_t i = 0; i < chunk_size; i++) { |
| // !ZX_HANDLE_OP_DUPLICATE is used to capture the case where we failed |
| // due to a bad operational arg. |
| if (local_handle_disposition[i].operation != ZX_HANDLE_OP_DUPLICATE) { |
| handles[i] = local_handle_disposition[i].handle; |
| } |
| } |
| return ZX_OK; |
| } |
| |
| // This overload is used by zx_channel_write. |
| zx::status<Handle*> get_handle_for_message_locked(ProcessDispatcher* process, |
| const Dispatcher* channel, |
| const zx_handle_t* handle_val) { |
| Handle* source = process->handle_table().GetHandleLocked(*process, *handle_val); |
| |
| auto status = handle_checks_locked(source, channel, ZX_HANDLE_OP_MOVE, ZX_RIGHT_SAME_RIGHTS, |
| ZX_OBJ_TYPE_NONE); |
| if (status != ZX_OK) |
| return zx::error(status); |
| |
| return zx::ok(process->handle_table().RemoveHandleLocked(source).release()); |
| } |
| |
| // This overload is used by zx_channel_write_etc. |
| zx::status<Handle*> get_handle_for_message_locked(ProcessDispatcher* process, |
| const Dispatcher* channel, |
| zx_handle_disposition_t* handle_disposition) { |
| Handle* source = process->handle_table().GetHandleLocked(*process, handle_disposition->handle); |
| |
| const zx_handle_op_t operation = handle_disposition->operation; |
| const zx_rights_t desired_rights = handle_disposition->rights; |
| const zx_obj_type_t type = handle_disposition->type; |
| |
| auto status = handle_checks_locked(source, channel, operation, desired_rights, type); |
| if (status != ZX_OK) { |
| handle_disposition->result = status; |
| return zx::error(status); |
| } |
| // This if() block is purely an optimization and can be removed without |
| // the rest of the function having to change. |
| if ((operation == ZX_HANDLE_OP_MOVE) && (desired_rights == ZX_RIGHT_SAME_RIGHTS)) { |
| return zx::ok(process->handle_table().RemoveHandleLocked(source).release()); |
| } |
| // For the non-optimized case, we always need to create a new handle because |
| // the rights are a const member of Handle. |
| const auto dest_rights = |
| (desired_rights == ZX_RIGHT_SAME_RIGHTS) ? source->rights() : desired_rights; |
| |
| auto raw_handle = Handle::Dup(source, dest_rights).release(); |
| if (!raw_handle) { |
| // It's possible for the dup operation to fail if we run out of handles exactly |
| // at this point. |
| return zx::error(ZX_ERR_NO_MEMORY); |
| } |
| |
| // Use !ZX_HANDLE_OP_DUPLICATE so that we handle the case where operation |
| // is an invalid value. |
| if (operation != ZX_HANDLE_OP_DUPLICATE) { |
| process->handle_table().RemoveHandleLocked(source); |
| } |
| return zx::ok(raw_handle); |
| } |