forked from SerenityOS/serenity
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathFixedStringBuffer.h
121 lines (107 loc) Β· 4.42 KB
/
FixedStringBuffer.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
/*
* Copyright (c) 2023, Liav A. <liavalb@hotmail.co.il>
*
* SPDX-License-Identifier: BSD-2-Clause
*/
#pragma once
#include <AK/Array.h>
#include <AK/StringBuilder.h>
#include <AK/StringView.h>
#include <AK/TypedTransfer.h>
#include <AK/Userspace.h>
#if defined(KERNEL) && !defined(PREKERNEL)
# 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:
[[nodiscard]] static ErrorOr<FixedStringBuffer<Size>> vformatted(StringView fmtstr, AK::TypeErasedFormatParams& params)
requires(Size < StringBuilder::inline_capacity)
{
StringBuilder builder { StringBuilder::UseInlineCapacityOnly::Yes };
TRY(AK::vformat(builder, fmtstr, params));
FixedStringBuffer<Size> buffer {};
buffer.store_characters(builder.string_view());
return buffer;
}
template<typename... Parameters>
[[nodiscard]] static ErrorOr<FixedStringBuffer<Size>> formatted(CheckedFormatString<Parameters...>&& fmtstr, Parameters const&... parameters)
requires(Size < StringBuilder::inline_capacity)
{
AK::VariadicFormatParams<AK::AllowDebugOnlyFormatters::No, Parameters...> variadic_format_parameters { parameters... };
return vformatted(fmtstr.view(), variadic_format_parameters);
}
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';
}
#if defined(KERNEL) && !defined(PREKERNEL)
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); }
Span<u8 const> span_view_ensuring_ending_null_char()
{
VERIFY(m_stored_length + 1 <= Size);
m_storage[m_stored_length] = '\0';
return Span<u8 const>(m_storage.data(), m_stored_length + 1);
}
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