From 0d30f558f4fab4f3053a218fc89b34df5cc673ac Mon Sep 17 00:00:00 2001 From: Liav A Date: Mon, 17 Jul 2023 09:20:51 +0300 Subject: [PATCH] AK+Kernel: Add the FixedStringBuffer class and StdLib functions for it This class encapsulates a fixed Array with compile-time size definition for storing ASCII characters. There are also new Kernel StdLib functions to copy user data into such objects so this class will be useful later on. --- AK/FixedStringBuffer.h | 97 +++++++++++++++++++++++++++++++++++++++++ Kernel/Library/StdLib.h | 21 +++++++++ 2 files changed, 118 insertions(+) create mode 100644 AK/FixedStringBuffer.h diff --git a/AK/FixedStringBuffer.h b/AK/FixedStringBuffer.h new file mode 100644 index 00000000000..1693096842b --- /dev/null +++ b/AK/FixedStringBuffer.h @@ -0,0 +1,97 @@ +/* + * Copyright (c) 2023, Liav A. + * + * SPDX-License-Identifier: BSD-2-Clause + */ + +#pragma once + +#include +#include +#include +#include + +#ifdef KERNEL +# include +# include +# include +#endif + +namespace AK { + +template +class FixedStringBuffer { +public: + void store_characters(StringView characters) + { + // NOTE: Only store the characters up to the first null terminator + // because we don't care about any further characters. + // This matches some expected behavior in the Kernel code, because + // technically userspace programs could send a syscall argument with + // multiple null terminators - we only care about the *first* chunk up to + // the first null terminator, if present at all. + size_t stored_length = 0; + for (; stored_length < min(Size, characters.length()); stored_length++) { + if (characters[stored_length] == '\0') + break; + m_storage[stored_length] = characters[stored_length]; + } + m_stored_length = stored_length; + // NOTE: Fill the rest of the array bytes with zeroes, just to be + // on the safe side. + // Technically, it means that a sent StringView could occupy the + // entire storage without any null terminators and that's OK as well. + for (size_t index = m_stored_length; index < Size; index++) + m_storage[index] = '\0'; + } + +#ifdef KERNEL + ErrorOr copy_characters_from_user(Userspace user_str, size_t user_str_size) + { + if (user_str_size > Size) + return EFAULT; + bool is_user = Kernel::Memory::is_user_range(user_str.vaddr(), user_str_size); + if (!is_user) + return EFAULT; + Kernel::SmapDisabler disabler; + void* fault_at; + ssize_t length = Kernel::safe_strnlen(user_str.unsafe_userspace_ptr(), user_str_size, fault_at); + if (length < 0) { + dbgln("FixedStringBuffer::copy_characters_into_storage({:p}, {}) failed at {} (strnlen)", static_cast(user_str.unsafe_userspace_ptr()), user_str_size, VirtualAddress { fault_at }); + return EFAULT; + } + if (!Kernel::safe_memcpy(m_storage.data(), user_str.unsafe_userspace_ptr(), (size_t)length, fault_at)) { + dbgln("FixedStringBuffer::copy_characters_into_storage({:p}, {}) failed at {} (memcpy)", static_cast(user_str.unsafe_userspace_ptr()), user_str_size, VirtualAddress { fault_at }); + return EFAULT; + } + m_stored_length = (size_t)length; + for (size_t index = m_stored_length; index < Size; index++) + m_storage[index] = '\0'; + return {}; + } +#endif + + Span storage() + { + return m_storage.span(); + } + StringView representable_view() const { return StringView(m_storage.data(), m_stored_length); } + + size_t fixed_length() const { return Size; } + size_t stored_length() const { return m_stored_length; } + + FixedStringBuffer() + { + m_storage.fill(0); + } + +private: + Array m_storage; + size_t m_stored_length { 0 }; +}; + +} + +#if USING_AK_GLOBALLY +using AK::FixedStringBuffer; +#endif diff --git a/Kernel/Library/StdLib.h b/Kernel/Library/StdLib.h index d9572d998ec..c1aa94ff9f0 100644 --- a/Kernel/Library/StdLib.h +++ b/Kernel/Library/StdLib.h @@ -1,5 +1,6 @@ /* * Copyright (c) 2018-2020, Andreas Kling + * Copyright (c) 2023, Liav A. * * SPDX-License-Identifier: BSD-2-Clause */ @@ -8,6 +9,7 @@ #include #include +#include #include #include #include @@ -16,6 +18,25 @@ #include ErrorOr> try_copy_kstring_from_user(Userspace, size_t); + +template +ErrorOr try_copy_string_from_user_into_fixed_string_buffer(Userspace user_str, FixedStringBuffer& buffer, size_t user_str_size) +{ + if (user_str_size > Size) + return E2BIG; + TRY(buffer.copy_characters_from_user(user_str, user_str_size)); + return {}; +} + +template +ErrorOr try_copy_name_from_user_into_fixed_string_buffer(Userspace user_str, FixedStringBuffer& buffer, size_t user_str_size) +{ + if (user_str_size > Size) + return ENAMETOOLONG; + TRY(buffer.copy_characters_from_user(user_str, user_str_size)); + return {}; +} + ErrorOr copy_time_from_user(timespec const*); ErrorOr copy_time_from_user(timeval const*); template