mirror of
https://github.com/RGBCube/serenity
synced 2025-07-27 04:27:45 +00:00
Kernel: Use FixedStringBuffer for fixed-length strings in syscalls
Using the kernel stack is preferable, especially when the examined strings should be limited to a reasonable length. This is a small improvement, because if we don't actually move these strings then we don't need to own heap allocations for them during the syscall handler function scope. In addition to that, some kernel strings are known to be limited, like the hostname string, for these strings we also can use FixedStringBuffer to store and copy to and from these buffers, without using any heap allocations at all.
This commit is contained in:
parent
3fd4997fc2
commit
d8b514873f
13 changed files with 100 additions and 46 deletions
|
@ -15,9 +15,15 @@ ErrorOr<FlatPtr> Process::sys$gethostname(Userspace<char*> buffer, size_t size)
|
|||
if (size > NumericLimits<ssize_t>::max())
|
||||
return EINVAL;
|
||||
return hostname().with_shared([&](auto const& name) -> ErrorOr<FlatPtr> {
|
||||
if (size < (name->length() + 1))
|
||||
// NOTE: To be able to copy a null-terminated string, we need at most
|
||||
// 65 characters to store and copy and not 64 here, to store the whole
|
||||
// hostname string + null terminator.
|
||||
FixedStringBuffer<UTSNAME_ENTRY_LEN> current_hostname {};
|
||||
current_hostname.store_characters(name.representable_view());
|
||||
auto name_view = current_hostname.representable_view();
|
||||
if (size < (name_view.length() + 1))
|
||||
return ENAMETOOLONG;
|
||||
TRY(copy_to_user(buffer, name->characters(), name->length() + 1));
|
||||
TRY(copy_to_user(buffer, name_view.characters_without_null_termination(), name_view.length() + 1));
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
@ -30,11 +36,9 @@ ErrorOr<FlatPtr> Process::sys$sethostname(Userspace<char const*> buffer, size_t
|
|||
auto credentials = this->credentials();
|
||||
if (!credentials->is_superuser())
|
||||
return EPERM;
|
||||
if (length > 64)
|
||||
return ENAMETOOLONG;
|
||||
auto new_name = TRY(try_copy_kstring_from_user(buffer, length));
|
||||
auto new_hostname = TRY(get_syscall_name_string_fixed_buffer<UTSNAME_ENTRY_LEN - 1>(buffer, length));
|
||||
hostname().with_exclusive([&](auto& name) {
|
||||
name = move(new_name);
|
||||
name.store_characters(new_hostname.representable_view());
|
||||
});
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/FixedStringBuffer.h>
|
||||
#include <Kernel/FileSystem/Custody.h>
|
||||
#include <Kernel/FileSystem/MountFile.h>
|
||||
#include <Kernel/FileSystem/VirtualFileSystem.h>
|
||||
|
@ -20,14 +21,15 @@ ErrorOr<FlatPtr> Process::sys$fsopen(Userspace<Syscall::SC_fsopen_params const*>
|
|||
if (!credentials->is_superuser())
|
||||
return Error::from_errno(EPERM);
|
||||
auto params = TRY(copy_typed_from_user(user_params));
|
||||
auto fs_type_string = TRY(try_copy_kstring_from_user(params.fs_type));
|
||||
// NOTE: 16 characters should be enough for any fstype today and in the future.
|
||||
auto fs_type_string = TRY(get_syscall_name_string_fixed_buffer<16>(params.fs_type));
|
||||
|
||||
// NOTE: If some userspace program uses MS_REMOUNT, return EINVAL to indicate that we never want this
|
||||
// flag to appear in the mount table...
|
||||
if (params.flags & MS_REMOUNT || params.flags & MS_BIND)
|
||||
return Error::from_errno(EINVAL);
|
||||
|
||||
auto const* fs_type_initializer = TRY(VirtualFileSystem::find_filesystem_type_initializer(fs_type_string->view()));
|
||||
auto const* fs_type_initializer = TRY(VirtualFileSystem::find_filesystem_type_initializer(fs_type_string.representable_view()));
|
||||
VERIFY(fs_type_initializer);
|
||||
auto mount_file = TRY(MountFile::create(*fs_type_initializer, params.flags));
|
||||
auto description = TRY(OpenFileDescription::try_create(move(mount_file)));
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
* SPDX-License-Identifier: BSD-2-Clause
|
||||
*/
|
||||
|
||||
#include <AK/FixedStringBuffer.h>
|
||||
#include <AK/StringView.h>
|
||||
#include <Kernel/Tasks/Process.h>
|
||||
|
||||
|
@ -14,17 +15,19 @@ ErrorOr<FlatPtr> Process::sys$pledge(Userspace<Syscall::SC_pledge_params const*>
|
|||
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
||||
auto params = TRY(copy_typed_from_user(user_params));
|
||||
|
||||
if (params.promises.length > 1024 || params.execpromises.length > 1024)
|
||||
return E2BIG;
|
||||
FixedStringBuffer<all_promises_strings_length_with_spaces> promises {};
|
||||
bool promises_provided { false };
|
||||
FixedStringBuffer<all_promises_strings_length_with_spaces> execpromises {};
|
||||
bool execpromises_provided { false };
|
||||
|
||||
OwnPtr<KString> promises;
|
||||
if (params.promises.characters) {
|
||||
promises = TRY(try_copy_kstring_from_user(params.promises));
|
||||
promises_provided = true;
|
||||
promises = TRY(get_syscall_string_fixed_buffer<all_promises_strings_length_with_spaces>(params.promises));
|
||||
}
|
||||
|
||||
OwnPtr<KString> execpromises;
|
||||
if (params.execpromises.characters) {
|
||||
execpromises = TRY(try_copy_kstring_from_user(params.execpromises));
|
||||
execpromises_provided = true;
|
||||
execpromises = TRY(get_syscall_string_fixed_buffer<all_promises_strings_length_with_spaces>(params.execpromises));
|
||||
}
|
||||
|
||||
auto parse_pledge = [&](auto pledge_spec, u32& mask) {
|
||||
|
@ -43,19 +46,19 @@ ErrorOr<FlatPtr> Process::sys$pledge(Userspace<Syscall::SC_pledge_params const*>
|
|||
};
|
||||
|
||||
u32 new_promises = 0;
|
||||
if (promises) {
|
||||
if (!parse_pledge(promises->view(), new_promises))
|
||||
if (promises_provided) {
|
||||
if (!parse_pledge(promises.representable_view(), new_promises))
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
u32 new_execpromises = 0;
|
||||
if (execpromises) {
|
||||
if (!parse_pledge(execpromises->view(), new_execpromises))
|
||||
if (execpromises_provided) {
|
||||
if (!parse_pledge(execpromises.representable_view(), new_execpromises))
|
||||
return EINVAL;
|
||||
}
|
||||
|
||||
return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
|
||||
if (promises) {
|
||||
if (promises_provided) {
|
||||
if (protected_data.has_promises && (new_promises & ~protected_data.promises)) {
|
||||
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
||||
return EPERM;
|
||||
|
@ -63,7 +66,7 @@ ErrorOr<FlatPtr> Process::sys$pledge(Userspace<Syscall::SC_pledge_params const*>
|
|||
}
|
||||
}
|
||||
|
||||
if (execpromises) {
|
||||
if (execpromises_provided) {
|
||||
if (protected_data.has_execpromises && (new_execpromises & ~protected_data.execpromises)) {
|
||||
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
||||
return EPERM;
|
||||
|
@ -76,12 +79,12 @@ ErrorOr<FlatPtr> Process::sys$pledge(Userspace<Syscall::SC_pledge_params const*>
|
|||
// erroring out when parsing the exec promises later. Such bugs silently
|
||||
// leave the caller in an unexpected state.
|
||||
|
||||
if (promises) {
|
||||
if (promises_provided) {
|
||||
protected_data.has_promises = true;
|
||||
protected_data.promises = new_promises;
|
||||
}
|
||||
|
||||
if (execpromises) {
|
||||
if (execpromises_provided) {
|
||||
protected_data.has_execpromises = true;
|
||||
protected_data.execpromises = new_execpromises;
|
||||
}
|
||||
|
|
|
@ -38,9 +38,10 @@ ErrorOr<FlatPtr> Process::sys$uname(Userspace<utsname*> user_buf)
|
|||
AK::TypedTransfer<u8>::copy(reinterpret_cast<u8*>(buf.version), SERENITY_VERSION.bytes().data(), min(SERENITY_VERSION.length(), UTSNAME_ENTRY_LEN - 1));
|
||||
|
||||
hostname().with_shared([&](auto const& name) {
|
||||
auto length = min(name->length(), UTSNAME_ENTRY_LEN - 1);
|
||||
AK::TypedTransfer<char>::copy(reinterpret_cast<char*>(buf.nodename), name->characters(), length);
|
||||
buf.nodename[length] = '\0';
|
||||
auto name_length = name.representable_view().length();
|
||||
VERIFY(name_length <= (UTSNAME_ENTRY_LEN - 1));
|
||||
AK::TypedTransfer<char>::copy(reinterpret_cast<char*>(buf.nodename), name.representable_view().characters_without_null_termination(), name_length);
|
||||
buf.nodename[name_length] = '\0';
|
||||
});
|
||||
|
||||
TRY(copy_to_user(user_buf, &buf));
|
||||
|
|
|
@ -101,19 +101,16 @@ ErrorOr<FlatPtr> Process::sys$unveil(Userspace<Syscall::SC_unveil_params const*>
|
|||
if (!params.path.characters || !params.permissions.characters)
|
||||
return EINVAL;
|
||||
|
||||
if (params.permissions.length > 5)
|
||||
return EINVAL;
|
||||
|
||||
auto path = TRY(get_syscall_path_argument(params.path));
|
||||
|
||||
if (path->is_empty() || !path->view().starts_with('/'))
|
||||
return EINVAL;
|
||||
|
||||
auto permissions = TRY(try_copy_kstring_from_user(params.permissions));
|
||||
auto permissions = TRY(get_syscall_string_fixed_buffer<5>(params.permissions));
|
||||
|
||||
// Let's work out permissions first...
|
||||
unsigned new_permissions = 0;
|
||||
for (char const permission : permissions->view()) {
|
||||
for (char const permission : permissions.representable_view()) {
|
||||
switch (permission) {
|
||||
case 'r':
|
||||
new_permissions |= UnveilAccess::Read;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue