mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 20:07:35 +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
|
@ -65,6 +65,7 @@ Promises marked with an asterisk (\*) are SerenityOS specific extensions not sup
|
||||||
* `EFAULT`: `promises` and/or `execpromises` are not null and not in readable memory.
|
* `EFAULT`: `promises` and/or `execpromises` are not null and not in readable memory.
|
||||||
* `EINVAL`: One or more invalid promises were specified.
|
* `EINVAL`: One or more invalid promises were specified.
|
||||||
* `EPERM`: An attempt to increase capabilities was rejected.
|
* `EPERM`: An attempt to increase capabilities was rejected.
|
||||||
|
* `E2BIG`: `promises` string or `execpromises `string are longer than all known promises strings together.
|
||||||
|
|
||||||
## History
|
## History
|
||||||
|
|
||||||
|
|
|
@ -68,6 +68,7 @@ the error.
|
||||||
* `EPERM`: The veil is locked, or an attempt to add more permissions for an
|
* `EPERM`: The veil is locked, or an attempt to add more permissions for an
|
||||||
already unveiled path was rejected.
|
already unveiled path was rejected.
|
||||||
* `EINVAL`: `path` is not an absolute path, or `permissions` are malformed.
|
* `EINVAL`: `path` is not an absolute path, or `permissions` are malformed.
|
||||||
|
* `E2BIG`: `permissions` string is longer than 5 characters.
|
||||||
|
|
||||||
All of the usual path resolution errors may also occur.
|
All of the usual path resolution errors may also occur.
|
||||||
|
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
#include <Kernel/API/Ioctl.h>
|
#include <Kernel/API/Ioctl.h>
|
||||||
#include <Kernel/API/POSIX/errno.h>
|
#include <Kernel/API/POSIX/errno.h>
|
||||||
#include <Kernel/API/POSIX/unistd.h>
|
#include <Kernel/API/POSIX/unistd.h>
|
||||||
|
#include <Kernel/API/Syscall.h>
|
||||||
#include <Kernel/FileSystem/Inode.h>
|
#include <Kernel/FileSystem/Inode.h>
|
||||||
#include <Kernel/FileSystem/MountFile.h>
|
#include <Kernel/FileSystem/MountFile.h>
|
||||||
#include <Kernel/FileSystem/OpenFileDescription.h>
|
#include <Kernel/FileSystem/OpenFileDescription.h>
|
||||||
|
@ -16,6 +17,7 @@
|
||||||
#include <Kernel/Library/StdLib.h>
|
#include <Kernel/Library/StdLib.h>
|
||||||
#include <Kernel/Memory/PrivateInodeVMObject.h>
|
#include <Kernel/Memory/PrivateInodeVMObject.h>
|
||||||
#include <Kernel/Memory/SharedInodeVMObject.h>
|
#include <Kernel/Memory/SharedInodeVMObject.h>
|
||||||
|
#include <Kernel/Tasks/Process.h>
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
|
@ -51,8 +53,10 @@ ErrorOr<void> MountFile::ioctl(OpenFileDescription&, unsigned request, Userspace
|
||||||
auto mount_specific_data = TRY(copy_typed_from_user(user_mount_specific_data));
|
auto mount_specific_data = TRY(copy_typed_from_user(user_mount_specific_data));
|
||||||
if ((mount_specific_data.value_type == MountSpecificFlag::ValueType::SignedInteger || mount_specific_data.value_type == MountSpecificFlag::ValueType::UnsignedInteger) && mount_specific_data.value_length != 8)
|
if ((mount_specific_data.value_type == MountSpecificFlag::ValueType::SignedInteger || mount_specific_data.value_type == MountSpecificFlag::ValueType::UnsignedInteger) && mount_specific_data.value_length != 8)
|
||||||
return EDOM;
|
return EDOM;
|
||||||
if (mount_specific_data.key_string_length > MOUNT_SPECIFIC_FLAG_KEY_STRING_MAX_LENGTH)
|
|
||||||
return ENAMETOOLONG;
|
Syscall::StringArgument user_key_string { reinterpret_cast<const char*>(mount_specific_data.key_string_addr), static_cast<size_t>(mount_specific_data.key_string_length) };
|
||||||
|
auto key_string = TRY(Process::get_syscall_name_string_fixed_buffer<MOUNT_SPECIFIC_FLAG_KEY_STRING_MAX_LENGTH>(user_key_string));
|
||||||
|
|
||||||
if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_length == 0)
|
if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_length == 0)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_addr == nullptr)
|
if (mount_specific_data.value_type != MountSpecificFlag::ValueType::Boolean && mount_specific_data.value_addr == nullptr)
|
||||||
|
@ -71,7 +75,6 @@ ErrorOr<void> MountFile::ioctl(OpenFileDescription&, unsigned request, Userspace
|
||||||
// NOTE: We enforce that the passed argument will be either i64 or u64, so it will always be
|
// NOTE: We enforce that the passed argument will be either i64 or u64, so it will always be
|
||||||
// exactly 8 bytes. We do that to simplify handling of integers as well as to ensure ABI correctness
|
// exactly 8 bytes. We do that to simplify handling of integers as well as to ensure ABI correctness
|
||||||
// in all possible cases.
|
// in all possible cases.
|
||||||
auto key_string = TRY(try_copy_kstring_from_user(reinterpret_cast<FlatPtr>(mount_specific_data.key_string_addr), static_cast<size_t>(mount_specific_data.key_string_length)));
|
|
||||||
switch (mount_specific_data.value_type) {
|
switch (mount_specific_data.value_type) {
|
||||||
// NOTE: This is actually considered as simply boolean flag.
|
// NOTE: This is actually considered as simply boolean flag.
|
||||||
case MountSpecificFlag::ValueType::Boolean: {
|
case MountSpecificFlag::ValueType::Boolean: {
|
||||||
|
@ -81,27 +84,27 @@ ErrorOr<void> MountFile::ioctl(OpenFileDescription&, unsigned request, Userspace
|
||||||
if (value_integer != 0 && value_integer != 1)
|
if (value_integer != 0 && value_integer != 1)
|
||||||
return EDOM;
|
return EDOM;
|
||||||
bool value = (value_integer == 1) ? true : false;
|
bool value = (value_integer == 1) ? true : false;
|
||||||
TRY(m_file_system_initializer.handle_mount_boolean_flag(our_mount_specific_data->bytes(), key_string->view(), value));
|
TRY(m_file_system_initializer.handle_mount_boolean_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case MountSpecificFlag::ValueType::UnsignedInteger: {
|
case MountSpecificFlag::ValueType::UnsignedInteger: {
|
||||||
VERIFY(m_file_system_initializer.handle_mount_unsigned_integer_flag);
|
VERIFY(m_file_system_initializer.handle_mount_unsigned_integer_flag);
|
||||||
Userspace<u64*> user_value_addr(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr));
|
Userspace<u64*> user_value_addr(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr));
|
||||||
auto value_integer = TRY(copy_typed_from_user(user_value_addr));
|
auto value_integer = TRY(copy_typed_from_user(user_value_addr));
|
||||||
TRY(m_file_system_initializer.handle_mount_unsigned_integer_flag(our_mount_specific_data->bytes(), key_string->view(), value_integer));
|
TRY(m_file_system_initializer.handle_mount_unsigned_integer_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_integer));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case MountSpecificFlag::ValueType::SignedInteger: {
|
case MountSpecificFlag::ValueType::SignedInteger: {
|
||||||
VERIFY(m_file_system_initializer.handle_mount_signed_integer_flag);
|
VERIFY(m_file_system_initializer.handle_mount_signed_integer_flag);
|
||||||
Userspace<i64*> user_value_addr(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr));
|
Userspace<i64*> user_value_addr(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr));
|
||||||
auto value_integer = TRY(copy_typed_from_user(user_value_addr));
|
auto value_integer = TRY(copy_typed_from_user(user_value_addr));
|
||||||
TRY(m_file_system_initializer.handle_mount_signed_integer_flag(our_mount_specific_data->bytes(), key_string->view(), value_integer));
|
TRY(m_file_system_initializer.handle_mount_signed_integer_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_integer));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
case MountSpecificFlag::ValueType::ASCIIString: {
|
case MountSpecificFlag::ValueType::ASCIIString: {
|
||||||
VERIFY(m_file_system_initializer.handle_mount_ascii_string_flag);
|
VERIFY(m_file_system_initializer.handle_mount_ascii_string_flag);
|
||||||
auto value_string = TRY(try_copy_kstring_from_user(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr), static_cast<size_t>(mount_specific_data.value_length)));
|
auto value_string = TRY(try_copy_kstring_from_user(reinterpret_cast<FlatPtr>(mount_specific_data.value_addr), static_cast<size_t>(mount_specific_data.value_length)));
|
||||||
TRY(m_file_system_initializer.handle_mount_ascii_string_flag(our_mount_specific_data->bytes(), key_string->view(), value_string->view()));
|
TRY(m_file_system_initializer.handle_mount_ascii_string_flag(our_mount_specific_data->bytes(), key_string.representable_view(), value_string->view()));
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -622,9 +622,8 @@ ErrorOr<void> IPv4Socket::ioctl(OpenFileDescription&, unsigned request, Userspac
|
||||||
TRY(copy_from_user(&route, user_route));
|
TRY(copy_from_user(&route, user_route));
|
||||||
|
|
||||||
Userspace<const char*> user_rt_dev((FlatPtr)route.rt_dev);
|
Userspace<const char*> user_rt_dev((FlatPtr)route.rt_dev);
|
||||||
auto ifname = TRY(try_copy_kstring_from_user(user_rt_dev, IFNAMSIZ));
|
auto ifname = TRY(Process::get_syscall_name_string_fixed_buffer<IFNAMSIZ>(user_rt_dev));
|
||||||
|
auto adapter = NetworkingManagement::the().lookup_by_name(ifname.representable_view());
|
||||||
auto adapter = NetworkingManagement::the().lookup_by_name(ifname->view());
|
|
||||||
if (!adapter)
|
if (!adapter)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
|
|
||||||
|
|
|
@ -96,8 +96,8 @@ ErrorOr<void> Socket::setsockopt(int level, int option, Userspace<void const*> u
|
||||||
if (user_value_size != IFNAMSIZ)
|
if (user_value_size != IFNAMSIZ)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
auto user_string = static_ptr_cast<char const*>(user_value);
|
auto user_string = static_ptr_cast<char const*>(user_value);
|
||||||
auto ifname = TRY(try_copy_kstring_from_user(user_string, user_value_size));
|
auto ifname = TRY(Process::get_syscall_name_string_fixed_buffer<IFNAMSIZ>(user_string, user_value_size));
|
||||||
auto device = NetworkingManagement::the().lookup_by_name(ifname->view());
|
auto device = NetworkingManagement::the().lookup_by_name(ifname.representable_view());
|
||||||
if (!device)
|
if (!device)
|
||||||
return ENODEV;
|
return ENODEV;
|
||||||
m_bound_interface.with([&device](auto& bound_device) {
|
m_bound_interface.with([&device](auto& bound_device) {
|
||||||
|
|
|
@ -15,9 +15,15 @@ ErrorOr<FlatPtr> Process::sys$gethostname(Userspace<char*> buffer, size_t size)
|
||||||
if (size > NumericLimits<ssize_t>::max())
|
if (size > NumericLimits<ssize_t>::max())
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
return hostname().with_shared([&](auto const& name) -> ErrorOr<FlatPtr> {
|
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;
|
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;
|
return 0;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -30,11 +36,9 @@ ErrorOr<FlatPtr> Process::sys$sethostname(Userspace<char const*> buffer, size_t
|
||||||
auto credentials = this->credentials();
|
auto credentials = this->credentials();
|
||||||
if (!credentials->is_superuser())
|
if (!credentials->is_superuser())
|
||||||
return EPERM;
|
return EPERM;
|
||||||
if (length > 64)
|
auto new_hostname = TRY(get_syscall_name_string_fixed_buffer<UTSNAME_ENTRY_LEN - 1>(buffer, length));
|
||||||
return ENAMETOOLONG;
|
|
||||||
auto new_name = TRY(try_copy_kstring_from_user(buffer, length));
|
|
||||||
hostname().with_exclusive([&](auto& name) {
|
hostname().with_exclusive([&](auto& name) {
|
||||||
name = move(new_name);
|
name.store_characters(new_hostname.representable_view());
|
||||||
});
|
});
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/FixedStringBuffer.h>
|
||||||
#include <Kernel/FileSystem/Custody.h>
|
#include <Kernel/FileSystem/Custody.h>
|
||||||
#include <Kernel/FileSystem/MountFile.h>
|
#include <Kernel/FileSystem/MountFile.h>
|
||||||
#include <Kernel/FileSystem/VirtualFileSystem.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())
|
if (!credentials->is_superuser())
|
||||||
return Error::from_errno(EPERM);
|
return Error::from_errno(EPERM);
|
||||||
auto params = TRY(copy_typed_from_user(user_params));
|
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
|
// NOTE: If some userspace program uses MS_REMOUNT, return EINVAL to indicate that we never want this
|
||||||
// flag to appear in the mount table...
|
// flag to appear in the mount table...
|
||||||
if (params.flags & MS_REMOUNT || params.flags & MS_BIND)
|
if (params.flags & MS_REMOUNT || params.flags & MS_BIND)
|
||||||
return Error::from_errno(EINVAL);
|
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);
|
VERIFY(fs_type_initializer);
|
||||||
auto mount_file = TRY(MountFile::create(*fs_type_initializer, params.flags));
|
auto mount_file = TRY(MountFile::create(*fs_type_initializer, params.flags));
|
||||||
auto description = TRY(OpenFileDescription::try_create(move(mount_file)));
|
auto description = TRY(OpenFileDescription::try_create(move(mount_file)));
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/FixedStringBuffer.h>
|
||||||
#include <AK/StringView.h>
|
#include <AK/StringView.h>
|
||||||
#include <Kernel/Tasks/Process.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);
|
VERIFY_NO_PROCESS_BIG_LOCK(this);
|
||||||
auto params = TRY(copy_typed_from_user(user_params));
|
auto params = TRY(copy_typed_from_user(user_params));
|
||||||
|
|
||||||
if (params.promises.length > 1024 || params.execpromises.length > 1024)
|
FixedStringBuffer<all_promises_strings_length_with_spaces> promises {};
|
||||||
return E2BIG;
|
bool promises_provided { false };
|
||||||
|
FixedStringBuffer<all_promises_strings_length_with_spaces> execpromises {};
|
||||||
|
bool execpromises_provided { false };
|
||||||
|
|
||||||
OwnPtr<KString> promises;
|
|
||||||
if (params.promises.characters) {
|
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) {
|
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) {
|
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;
|
u32 new_promises = 0;
|
||||||
if (promises) {
|
if (promises_provided) {
|
||||||
if (!parse_pledge(promises->view(), new_promises))
|
if (!parse_pledge(promises.representable_view(), new_promises))
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
u32 new_execpromises = 0;
|
u32 new_execpromises = 0;
|
||||||
if (execpromises) {
|
if (execpromises_provided) {
|
||||||
if (!parse_pledge(execpromises->view(), new_execpromises))
|
if (!parse_pledge(execpromises.representable_view(), new_execpromises))
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return with_mutable_protected_data([&](auto& protected_data) -> ErrorOr<FlatPtr> {
|
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.has_promises && (new_promises & ~protected_data.promises)) {
|
||||||
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
||||||
return EPERM;
|
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.has_execpromises && (new_execpromises & ~protected_data.execpromises)) {
|
||||||
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
if (!(protected_data.promises & (1u << (u32)Pledge::no_error)))
|
||||||
return EPERM;
|
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
|
// erroring out when parsing the exec promises later. Such bugs silently
|
||||||
// leave the caller in an unexpected state.
|
// leave the caller in an unexpected state.
|
||||||
|
|
||||||
if (promises) {
|
if (promises_provided) {
|
||||||
protected_data.has_promises = true;
|
protected_data.has_promises = true;
|
||||||
protected_data.promises = new_promises;
|
protected_data.promises = new_promises;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (execpromises) {
|
if (execpromises_provided) {
|
||||||
protected_data.has_execpromises = true;
|
protected_data.has_execpromises = true;
|
||||||
protected_data.execpromises = new_execpromises;
|
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));
|
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) {
|
hostname().with_shared([&](auto const& name) {
|
||||||
auto length = min(name->length(), UTSNAME_ENTRY_LEN - 1);
|
auto name_length = name.representable_view().length();
|
||||||
AK::TypedTransfer<char>::copy(reinterpret_cast<char*>(buf.nodename), name->characters(), length);
|
VERIFY(name_length <= (UTSNAME_ENTRY_LEN - 1));
|
||||||
buf.nodename[length] = '\0';
|
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));
|
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)
|
if (!params.path.characters || !params.permissions.characters)
|
||||||
return EINVAL;
|
return EINVAL;
|
||||||
|
|
||||||
if (params.permissions.length > 5)
|
|
||||||
return EINVAL;
|
|
||||||
|
|
||||||
auto path = TRY(get_syscall_path_argument(params.path));
|
auto path = TRY(get_syscall_path_argument(params.path));
|
||||||
|
|
||||||
if (path->is_empty() || !path->view().starts_with('/'))
|
if (path->is_empty() || !path->view().starts_with('/'))
|
||||||
return EINVAL;
|
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...
|
// Let's work out permissions first...
|
||||||
unsigned new_permissions = 0;
|
unsigned new_permissions = 0;
|
||||||
for (char const permission : permissions->view()) {
|
for (char const permission : permissions.representable_view()) {
|
||||||
switch (permission) {
|
switch (permission) {
|
||||||
case 'r':
|
case 'r':
|
||||||
new_permissions |= UnveilAccess::Read;
|
new_permissions |= UnveilAccess::Read;
|
||||||
|
|
|
@ -53,9 +53,9 @@ static Atomic<pid_t> next_pid;
|
||||||
static Singleton<SpinlockProtected<Process::AllProcessesList, LockRank::None>> s_all_instances;
|
static Singleton<SpinlockProtected<Process::AllProcessesList, LockRank::None>> s_all_instances;
|
||||||
READONLY_AFTER_INIT Memory::Region* g_signal_trampoline_region;
|
READONLY_AFTER_INIT Memory::Region* g_signal_trampoline_region;
|
||||||
|
|
||||||
static Singleton<MutexProtected<OwnPtr<KString>>> s_hostname;
|
static Singleton<MutexProtected<FixedStringBuffer<UTSNAME_ENTRY_LEN - 1>>> s_hostname;
|
||||||
|
|
||||||
MutexProtected<OwnPtr<KString>>& hostname()
|
MutexProtected<FixedStringBuffer<UTSNAME_ENTRY_LEN - 1>>& hostname()
|
||||||
{
|
{
|
||||||
return *s_hostname;
|
return *s_hostname;
|
||||||
}
|
}
|
||||||
|
@ -161,7 +161,7 @@ UNMAP_AFTER_INIT void Process::initialize()
|
||||||
|
|
||||||
// Note: This is called before scheduling is initialized, and before APs are booted.
|
// Note: This is called before scheduling is initialized, and before APs are booted.
|
||||||
// So we can "safely" bypass the lock here.
|
// So we can "safely" bypass the lock here.
|
||||||
reinterpret_cast<OwnPtr<KString>&>(hostname()) = KString::must_create("courage"sv);
|
reinterpret_cast<FixedStringBuffer<UTSNAME_ENTRY_LEN - 1>&>(hostname()).store_characters("courage"sv);
|
||||||
|
|
||||||
create_signal_trampoline();
|
create_signal_trampoline();
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,7 +41,7 @@
|
||||||
|
|
||||||
namespace Kernel {
|
namespace Kernel {
|
||||||
|
|
||||||
MutexProtected<OwnPtr<KString>>& hostname();
|
MutexProtected<FixedStringBuffer<UTSNAME_ENTRY_LEN - 1>>& hostname();
|
||||||
UnixDateTime kgettimeofday();
|
UnixDateTime kgettimeofday();
|
||||||
|
|
||||||
#define ENUMERATE_PLEDGE_PROMISES \
|
#define ENUMERATE_PLEDGE_PROMISES \
|
||||||
|
@ -74,6 +74,15 @@ UnixDateTime kgettimeofday();
|
||||||
__ENUMERATE_PLEDGE_PROMISE(mount) \
|
__ENUMERATE_PLEDGE_PROMISE(mount) \
|
||||||
__ENUMERATE_PLEDGE_PROMISE(no_error)
|
__ENUMERATE_PLEDGE_PROMISE(no_error)
|
||||||
|
|
||||||
|
#define __ENUMERATE_PLEDGE_PROMISE(x) sizeof(#x) + 1 +
|
||||||
|
// NOTE: We truncate the last space from the string as it's not needed (with 0 - 1).
|
||||||
|
constexpr static unsigned all_promises_strings_length_with_spaces = ENUMERATE_PLEDGE_PROMISES 0 - 1;
|
||||||
|
#undef __ENUMERATE_PLEDGE_PROMISE
|
||||||
|
|
||||||
|
// NOTE: This is a sanity check because length of more than 1024 characters
|
||||||
|
// is not reasonable.
|
||||||
|
static_assert(all_promises_strings_length_with_spaces <= 1024);
|
||||||
|
|
||||||
enum class Pledge : u32 {
|
enum class Pledge : u32 {
|
||||||
#define __ENUMERATE_PLEDGE_PROMISE(x) x,
|
#define __ENUMERATE_PLEDGE_PROMISE(x) x,
|
||||||
ENUMERATE_PLEDGE_PROMISES
|
ENUMERATE_PLEDGE_PROMISES
|
||||||
|
@ -605,6 +614,36 @@ public:
|
||||||
ErrorOr<void> validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region = nullptr) const;
|
ErrorOr<void> validate_mmap_prot(int prot, bool map_stack, bool map_anonymous, Memory::Region const* region = nullptr) const;
|
||||||
ErrorOr<void> validate_inode_mmap_prot(int prot, bool description_readable, bool description_writable, bool map_shared) const;
|
ErrorOr<void> validate_inode_mmap_prot(int prot, bool description_readable, bool description_writable, bool map_shared) const;
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
static ErrorOr<FixedStringBuffer<Size>> get_syscall_string_fixed_buffer(Syscall::StringArgument const& argument)
|
||||||
|
{
|
||||||
|
// NOTE: If the string is too much big for the FixedStringBuffer,
|
||||||
|
// we return E2BIG error here.
|
||||||
|
FixedStringBuffer<Size> buffer;
|
||||||
|
TRY(try_copy_string_from_user_into_fixed_string_buffer<Size>(reinterpret_cast<FlatPtr>(argument.characters), buffer, argument.length));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
static ErrorOr<FixedStringBuffer<Size>> get_syscall_name_string_fixed_buffer(Userspace<char const*> user_buffer, size_t user_length = Size)
|
||||||
|
{
|
||||||
|
// NOTE: If the string is too much big for the FixedStringBuffer,
|
||||||
|
// we return E2BIG error here.
|
||||||
|
FixedStringBuffer<Size> buffer;
|
||||||
|
TRY(try_copy_string_from_user_into_fixed_string_buffer<Size>(user_buffer, buffer, user_length));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t Size>
|
||||||
|
static ErrorOr<FixedStringBuffer<Size>> get_syscall_name_string_fixed_buffer(Syscall::StringArgument const& argument)
|
||||||
|
{
|
||||||
|
// NOTE: If the string is too much big for the FixedStringBuffer,
|
||||||
|
// we return ENAMETOOLONG error here.
|
||||||
|
FixedStringBuffer<Size> buffer;
|
||||||
|
TRY(try_copy_name_from_user_into_fixed_string_buffer<Size>(reinterpret_cast<FlatPtr>(argument.characters), buffer, argument.length));
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MemoryManager;
|
friend class MemoryManager;
|
||||||
friend class Scheduler;
|
friend class Scheduler;
|
||||||
|
|
|
@ -12,6 +12,10 @@ TEST_CASE(test_argument_validation)
|
||||||
{
|
{
|
||||||
auto res = unveil("/etc", "aaaaaaaaaaaa");
|
auto res = unveil("/etc", "aaaaaaaaaaaa");
|
||||||
EXPECT_EQ(res, -1);
|
EXPECT_EQ(res, -1);
|
||||||
|
EXPECT_EQ(errno, E2BIG);
|
||||||
|
|
||||||
|
res = unveil("/etc", "aaaaa");
|
||||||
|
EXPECT_EQ(res, -1);
|
||||||
EXPECT_EQ(errno, EINVAL);
|
EXPECT_EQ(errno, EINVAL);
|
||||||
|
|
||||||
res = unveil(nullptr, "r");
|
res = unveil(nullptr, "r");
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue