summaryrefslogtreecommitdiff
path: root/Kernel/StdLib.cpp
diff options
context:
space:
mode:
authorAndreas Kling <kling@serenityos.org>2021-05-28 09:29:16 +0200
committerAndreas Kling <kling@serenityos.org>2021-05-28 09:37:09 +0200
commit856f20f91f0558efb08a376a842c48ca183bb5be (patch)
tree6a34fbab8ec10e27a1a2bd8d878f812d45b31479 /Kernel/StdLib.cpp
parent279383a8f3cc9b9570a329ee7b0ed8844d26ba27 (diff)
downloadserenity-856f20f91f0558efb08a376a842c48ca183bb5be.zip
Kernel: Add try_copy_kstring_from_user()
This is a convenience function that works the same as our old copy_string_from_user(), but this returns a KString (and can fail!)
Diffstat (limited to 'Kernel/StdLib.cpp')
-rw-r--r--Kernel/StdLib.cpp34
1 files changed, 34 insertions, 0 deletions
diff --git a/Kernel/StdLib.cpp b/Kernel/StdLib.cpp
index 6fbe4dfd04..183480dcc1 100644
--- a/Kernel/StdLib.cpp
+++ b/Kernel/StdLib.cpp
@@ -43,6 +43,40 @@ String copy_string_from_user(Userspace<const char*> user_str, size_t user_str_si
return copy_string_from_user(user_str.unsafe_userspace_ptr(), user_str_size);
}
+Kernel::KResultOr<NonnullOwnPtr<Kernel::KString>> try_copy_kstring_from_user(const char* user_str, size_t user_str_size)
+{
+ bool is_user = Kernel::is_user_range(VirtualAddress(user_str), user_str_size);
+ if (!is_user)
+ return EFAULT;
+ Kernel::SmapDisabler disabler;
+ void* fault_at;
+ ssize_t length = Kernel::safe_strnlen(user_str, user_str_size, fault_at);
+ if (length < 0) {
+ dbgln("copy_kstring_from_user({:p}, {}) failed at {} (strnlen)", static_cast<const void*>(user_str), user_str_size, VirtualAddress { fault_at });
+ return EFAULT;
+ }
+ char* buffer;
+ auto new_string = Kernel::KString::try_create_uninitialized(length, buffer);
+ if (!new_string)
+ return ENOMEM;
+
+ buffer[length] = '\0';
+
+ if (length == 0)
+ return new_string.release_nonnull();
+
+ if (!Kernel::safe_memcpy(buffer, user_str, (size_t)length, fault_at)) {
+ dbgln("copy_kstring_from_user({:p}, {}) failed at {} (memcpy)", static_cast<const void*>(user_str), user_str_size, VirtualAddress { fault_at });
+ return EFAULT;
+ }
+ return new_string.release_nonnull();
+}
+
+Kernel::KResultOr<NonnullOwnPtr<Kernel::KString>> try_copy_kstring_from_user(Userspace<const char*> user_str, size_t user_str_size)
+{
+ return try_copy_kstring_from_user(user_str.unsafe_userspace_ptr(), user_str_size);
+}
+
[[nodiscard]] Optional<Time> copy_time_from_user(const timespec* ts_user)
{
timespec ts;