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.
This commit is contained in:
Liav A 2023-07-17 09:20:51 +03:00 committed by Andrew Kaster
parent 3b09560251
commit 0d30f558f4
Notes: sideshowbarker 2024-07-17 09:49:33 +09:00
2 changed files with 118 additions and 0 deletions

97
AK/FixedStringBuffer.h Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/StringView.h>
#include <AK/TypedTransfer.h>
#include <AK/Userspace.h>
#ifdef KERNEL
# include <Kernel/Arch/SafeMem.h>
# include <Kernel/Arch/SmapDisabler.h>
# include <Kernel/Memory/MemorySections.h>
#endif
namespace AK {
template<size_t Size>
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<void> copy_characters_from_user(Userspace<char const*> 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<void const*>(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<void const*>(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<u8> 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<u8, Size> m_storage;
size_t m_stored_length { 0 };
};
}
#if USING_AK_GLOBALLY
using AK::FixedStringBuffer;
#endif

View File

@ -1,5 +1,6 @@
/*
* Copyright (c) 2018-2020, Andreas Kling <kling@serenityos.org>
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
@ -8,6 +9,7 @@
#include <AK/Checked.h>
#include <AK/Error.h>
#include <AK/FixedStringBuffer.h>
#include <AK/Forward.h>
#include <AK/Time.h>
#include <AK/Userspace.h>
@ -16,6 +18,25 @@
#include <stddef.h>
ErrorOr<NonnullOwnPtr<Kernel::KString>> try_copy_kstring_from_user(Userspace<char const*>, size_t);
template<size_t Size>
ErrorOr<void> try_copy_string_from_user_into_fixed_string_buffer(Userspace<char const*> user_str, FixedStringBuffer<Size>& 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<size_t Size>
ErrorOr<void> try_copy_name_from_user_into_fixed_string_buffer(Userspace<char const*> user_str, FixedStringBuffer<Size>& 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<Duration> copy_time_from_user(timespec const*);
ErrorOr<Duration> copy_time_from_user(timeval const*);
template<typename T>