Merge pull request #2161 from lioncash/handle-table
kernel/handle_table: Allow process capabilities to limit the handle table size
This commit is contained in:
		@@ -14,6 +14,7 @@ constexpr ResultCode ERR_MAX_CONNECTIONS_REACHED{ErrorModule::Kernel, 7};
 | 
			
		||||
constexpr ResultCode ERR_INVALID_CAPABILITY_DESCRIPTOR{ErrorModule::Kernel, 14};
 | 
			
		||||
constexpr ResultCode ERR_INVALID_SIZE{ErrorModule::Kernel, 101};
 | 
			
		||||
constexpr ResultCode ERR_INVALID_ADDRESS{ErrorModule::Kernel, 102};
 | 
			
		||||
constexpr ResultCode ERR_OUT_OF_MEMORY{ErrorModule::Kernel, 104};
 | 
			
		||||
constexpr ResultCode ERR_HANDLE_TABLE_FULL{ErrorModule::Kernel, 105};
 | 
			
		||||
constexpr ResultCode ERR_INVALID_ADDRESS_STATE{ErrorModule::Kernel, 106};
 | 
			
		||||
constexpr ResultCode ERR_INVALID_MEMORY_PERMISSIONS{ErrorModule::Kernel, 108};
 | 
			
		||||
 
 | 
			
		||||
@@ -14,32 +14,47 @@
 | 
			
		||||
namespace Kernel {
 | 
			
		||||
namespace {
 | 
			
		||||
constexpr u16 GetSlot(Handle handle) {
 | 
			
		||||
    return handle >> 15;
 | 
			
		||||
    return static_cast<u16>(handle >> 15);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
constexpr u16 GetGeneration(Handle handle) {
 | 
			
		||||
    return handle & 0x7FFF;
 | 
			
		||||
    return static_cast<u16>(handle & 0x7FFF);
 | 
			
		||||
}
 | 
			
		||||
} // Anonymous namespace
 | 
			
		||||
 | 
			
		||||
HandleTable::HandleTable() {
 | 
			
		||||
    next_generation = 1;
 | 
			
		||||
    Clear();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
HandleTable::~HandleTable() = default;
 | 
			
		||||
 | 
			
		||||
ResultCode HandleTable::SetSize(s32 handle_table_size) {
 | 
			
		||||
    if (static_cast<u32>(handle_table_size) > MAX_COUNT) {
 | 
			
		||||
        return ERR_OUT_OF_MEMORY;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    // Values less than or equal to zero indicate to use the maximum allowable
 | 
			
		||||
    // size for the handle table in the actual kernel, so we ignore the given
 | 
			
		||||
    // value in that case, since we assume this by default unless this function
 | 
			
		||||
    // is called.
 | 
			
		||||
    if (handle_table_size > 0) {
 | 
			
		||||
        table_size = static_cast<u16>(handle_table_size);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultVal<Handle> HandleTable::Create(SharedPtr<Object> obj) {
 | 
			
		||||
    DEBUG_ASSERT(obj != nullptr);
 | 
			
		||||
 | 
			
		||||
    u16 slot = next_free_slot;
 | 
			
		||||
    if (slot >= generations.size()) {
 | 
			
		||||
    const u16 slot = next_free_slot;
 | 
			
		||||
    if (slot >= table_size) {
 | 
			
		||||
        LOG_ERROR(Kernel, "Unable to allocate Handle, too many slots in use.");
 | 
			
		||||
        return ERR_HANDLE_TABLE_FULL;
 | 
			
		||||
    }
 | 
			
		||||
    next_free_slot = generations[slot];
 | 
			
		||||
 | 
			
		||||
    u16 generation = next_generation++;
 | 
			
		||||
    const u16 generation = next_generation++;
 | 
			
		||||
 | 
			
		||||
    // Overflow count so it fits in the 15 bits dedicated to the generation in the handle.
 | 
			
		||||
    // Horizon OS uses zero to represent an invalid handle, so skip to 1.
 | 
			
		||||
@@ -64,10 +79,11 @@ ResultVal<Handle> HandleTable::Duplicate(Handle handle) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
ResultCode HandleTable::Close(Handle handle) {
 | 
			
		||||
    if (!IsValid(handle))
 | 
			
		||||
    if (!IsValid(handle)) {
 | 
			
		||||
        return ERR_INVALID_HANDLE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    u16 slot = GetSlot(handle);
 | 
			
		||||
    const u16 slot = GetSlot(handle);
 | 
			
		||||
 | 
			
		||||
    objects[slot] = nullptr;
 | 
			
		||||
 | 
			
		||||
@@ -77,10 +93,10 @@ ResultCode HandleTable::Close(Handle handle) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
bool HandleTable::IsValid(Handle handle) const {
 | 
			
		||||
    std::size_t slot = GetSlot(handle);
 | 
			
		||||
    u16 generation = GetGeneration(handle);
 | 
			
		||||
    const std::size_t slot = GetSlot(handle);
 | 
			
		||||
    const u16 generation = GetGeneration(handle);
 | 
			
		||||
 | 
			
		||||
    return slot < MAX_COUNT && objects[slot] != nullptr && generations[slot] == generation;
 | 
			
		||||
    return slot < table_size && objects[slot] != nullptr && generations[slot] == generation;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
 | 
			
		||||
@@ -97,7 +113,7 @@ SharedPtr<Object> HandleTable::GetGeneric(Handle handle) const {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HandleTable::Clear() {
 | 
			
		||||
    for (u16 i = 0; i < MAX_COUNT; ++i) {
 | 
			
		||||
    for (u16 i = 0; i < table_size; ++i) {
 | 
			
		||||
        generations[i] = i + 1;
 | 
			
		||||
        objects[i] = nullptr;
 | 
			
		||||
    }
 | 
			
		||||
 
 | 
			
		||||
@@ -49,6 +49,20 @@ public:
 | 
			
		||||
    HandleTable();
 | 
			
		||||
    ~HandleTable();
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Sets the number of handles that may be in use at one time
 | 
			
		||||
     * for this handle table.
 | 
			
		||||
     *
 | 
			
		||||
     * @param handle_table_size The desired size to limit the handle table to.
 | 
			
		||||
     *
 | 
			
		||||
     * @returns an error code indicating if initialization was successful.
 | 
			
		||||
     *          If initialization was not successful, then ERR_OUT_OF_MEMORY
 | 
			
		||||
     *          will be returned.
 | 
			
		||||
     *
 | 
			
		||||
     * @pre handle_table_size must be within the range [0, 1024]
 | 
			
		||||
     */
 | 
			
		||||
    ResultCode SetSize(s32 handle_table_size);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Allocates a handle for the given object.
 | 
			
		||||
     * @return The created Handle or one of the following errors:
 | 
			
		||||
@@ -103,14 +117,21 @@ private:
 | 
			
		||||
     */
 | 
			
		||||
    std::array<u16, MAX_COUNT> generations;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * The limited size of the handle table. This can be specified by process
 | 
			
		||||
     * capabilities in order to restrict the overall number of handles that
 | 
			
		||||
     * can be created in a process instance
 | 
			
		||||
     */
 | 
			
		||||
    u16 table_size = static_cast<u16>(MAX_COUNT);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Global counter of the number of created handles. Stored in `generations` when a handle is
 | 
			
		||||
     * created, and wraps around to 1 when it hits 0x8000.
 | 
			
		||||
     */
 | 
			
		||||
    u16 next_generation;
 | 
			
		||||
    u16 next_generation = 1;
 | 
			
		||||
 | 
			
		||||
    /// Head of the free slots linked list.
 | 
			
		||||
    u16 next_free_slot;
 | 
			
		||||
    u16 next_free_slot = 0;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
} // namespace Kernel
 | 
			
		||||
 
 | 
			
		||||
@@ -99,7 +99,13 @@ ResultCode Process::LoadFromMetadata(const FileSys::ProgramMetadata& metadata) {
 | 
			
		||||
    vm_manager.Reset(metadata.GetAddressSpaceType());
 | 
			
		||||
 | 
			
		||||
    const auto& caps = metadata.GetKernelCapabilities();
 | 
			
		||||
    return capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
 | 
			
		||||
    const auto capability_init_result =
 | 
			
		||||
        capabilities.InitializeForUserProcess(caps.data(), caps.size(), vm_manager);
 | 
			
		||||
    if (capability_init_result.IsError()) {
 | 
			
		||||
        return capability_init_result;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    return handle_table.SetSize(capabilities.GetHandleTableSize());
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void Process::Run(VAddr entry_point, s32 main_thread_priority, u32 stack_size) {
 | 
			
		||||
 
 | 
			
		||||
@@ -96,7 +96,7 @@ void ProcessCapabilities::InitializeForMetadatalessProcess() {
 | 
			
		||||
    interrupt_capabilities.set();
 | 
			
		||||
 | 
			
		||||
    // Allow using the maximum possible amount of handles
 | 
			
		||||
    handle_table_size = static_cast<u32>(HandleTable::MAX_COUNT);
 | 
			
		||||
    handle_table_size = static_cast<s32>(HandleTable::MAX_COUNT);
 | 
			
		||||
 | 
			
		||||
    // Allow all debugging capabilities.
 | 
			
		||||
    is_debuggable = true;
 | 
			
		||||
@@ -337,7 +337,7 @@ ResultCode ProcessCapabilities::HandleHandleTableFlags(u32 flags) {
 | 
			
		||||
        return ERR_RESERVED_VALUE;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    handle_table_size = (flags >> 16) & 0x3FF;
 | 
			
		||||
    handle_table_size = static_cast<s32>((flags >> 16) & 0x3FF);
 | 
			
		||||
    return RESULT_SUCCESS;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -156,7 +156,7 @@ public:
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /// Gets the number of total allowable handles for the process' handle table.
 | 
			
		||||
    u32 GetHandleTableSize() const {
 | 
			
		||||
    s32 GetHandleTableSize() const {
 | 
			
		||||
        return handle_table_size;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
@@ -252,7 +252,7 @@ private:
 | 
			
		||||
    u64 core_mask = 0;
 | 
			
		||||
    u64 priority_mask = 0;
 | 
			
		||||
 | 
			
		||||
    u32 handle_table_size = 0;
 | 
			
		||||
    s32 handle_table_size = 0;
 | 
			
		||||
    u32 kernel_version = 0;
 | 
			
		||||
 | 
			
		||||
    ProgramType program_type = ProgramType::SysModule;
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user