Просмотр исходного кода

Kernel: Use Userspace<T> for the getsockopt syscall and Socket interface

The way getsockopt is implemented for socket types requires us to push
down Userspace<T> using into those interfaces. This change does so, and
utilizes proper copy implementations instead of the kind of haphazard
pointer dereferencing that was occurring there before.
Brian Gianforcaro 5 лет назад
Родитель
Сommit
30b2c0dc85

+ 2 - 2
Kernel/API/Syscall.h

@@ -306,8 +306,8 @@ struct SC_getsockopt_params {
     int sockfd;
     int level;
     int option;
-    void* value;
-    socklen_t* value_size;
+    Userspace<void*> value;
+    Userspace<socklen_t*> value_size;
 };
 
 struct SC_setsockopt_params {

+ 9 - 3
Kernel/Net/IPv4Socket.cpp

@@ -451,16 +451,22 @@ KResult IPv4Socket::setsockopt(int level, int option, Userspace<const void*> use
     }
 }
 
-KResult IPv4Socket::getsockopt(FileDescription& description, int level, int option, void* value, socklen_t* value_size)
+KResult IPv4Socket::getsockopt(FileDescription& description, int level, int option, Userspace<void*> value, Userspace<socklen_t*> value_size)
 {
     if (level != IPPROTO_IP)
         return Socket::getsockopt(description, level, option, value, value_size);
 
+    socklen_t size;
+    if (!Process::current()->validate_read_and_copy_typed(&size, value_size))
+        return KResult(-EFAULT);
+
     switch (option) {
     case IP_TTL:
-        if (*value_size < sizeof(int))
+        if (size < sizeof(int))
             return KResult(-EINVAL);
-        *(int*)value = m_ttl;
+        copy_to_user(static_ptr_cast<int*>(value), (int*)&m_ttl);
+        size = sizeof(int);
+        copy_to_user(value_size, &size);
         return KSuccess;
     default:
         return KResult(-ENOPROTOOPT);

+ 1 - 1
Kernel/Net/IPv4Socket.h

@@ -61,7 +61,7 @@ public:
     virtual KResultOr<size_t> sendto(FileDescription&, const void*, size_t, int, const sockaddr*, socklen_t) override;
     virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) override;
     virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t) override;
-    virtual KResult getsockopt(FileDescription&, int level, int option, void*, socklen_t*) override;
+    virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override;
 
     virtual int ioctl(FileDescription&, unsigned request, FlatPtr arg) override;
 

+ 13 - 7
Kernel/Net/LocalSocket.cpp

@@ -344,24 +344,30 @@ String LocalSocket::absolute_path(const FileDescription& description) const
     return builder.to_string();
 }
 
-KResult LocalSocket::getsockopt(FileDescription& description, int level, int option, void* value, socklen_t* value_size)
+KResult LocalSocket::getsockopt(FileDescription& description, int level, int option, Userspace<void*> value, Userspace<socklen_t*> value_size)
 {
     if (level != SOL_SOCKET)
         return Socket::getsockopt(description, level, option, value, value_size);
 
+
+    socklen_t size;
+    if (!Process::current()->validate_read_and_copy_typed(&size, value_size))
+        return KResult(-EFAULT);
+
     switch (option) {
     case SO_PEERCRED: {
-        if (*value_size < sizeof(ucred))
+        if (size < sizeof(ucred))
             return KResult(-EINVAL);
-        auto& creds = *(ucred*)value;
         switch (role(description)) {
         case Role::Accepted:
-            creds = m_origin;
-            *value_size = sizeof(ucred);
+            copy_to_user(static_ptr_cast<ucred*>(value), &m_origin);
+            size = sizeof(ucred);
+            copy_to_user(value_size, &size);
             return KSuccess;
         case Role::Connected:
-            creds = m_acceptor;
-            *value_size = sizeof(ucred);
+            copy_to_user(static_ptr_cast<ucred*>(value), &m_acceptor);
+            size = sizeof(ucred);
+            copy_to_user(value_size, &size);
             return KSuccess;
         case Role::Connecting:
             return KResult(-ENOTCONN);

+ 1 - 1
Kernel/Net/LocalSocket.h

@@ -62,7 +62,7 @@ public:
     virtual bool can_write(const FileDescription&, size_t) const override;
     virtual KResultOr<size_t> sendto(FileDescription&, const void*, size_t, int, const sockaddr*, socklen_t) override;
     virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) override;
-    virtual KResult getsockopt(FileDescription&, int level, int option, void*, socklen_t*) override;
+    virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>) override;
     virtual KResult chown(FileDescription&, uid_t, gid_t) override;
     virtual KResult chmod(FileDescription&, mode_t) override;
 

+ 27 - 15
Kernel/Net/Socket.cpp

@@ -137,40 +137,52 @@ KResult Socket::setsockopt(int level, int option, Userspace<const void*> user_va
     }
 }
 
-KResult Socket::getsockopt(FileDescription&, int level, int option, void* value, socklen_t* value_size)
+KResult Socket::getsockopt(FileDescription&, int level, int option, Userspace<void*> value, Userspace<socklen_t*> value_size)
 {
+    socklen_t size;
+    if (!Process::current()->validate_read_and_copy_typed(&size, value_size))
+        return KResult(-EFAULT);
+
     ASSERT(level == SOL_SOCKET);
     switch (option) {
     case SO_SNDTIMEO:
-        if (*value_size < sizeof(timeval))
+        if (size < sizeof(timeval))
             return KResult(-EINVAL);
-        *(timeval*)value = m_send_timeout;
-        *value_size = sizeof(timeval);
+        copy_to_user(static_ptr_cast<timeval*>(value), &m_send_timeout);
+        size = sizeof(timeval);
+        copy_to_user(value_size, &size);
         return KSuccess;
     case SO_RCVTIMEO:
-        if (*value_size < sizeof(timeval))
+        if (size < sizeof(timeval))
             return KResult(-EINVAL);
-        *(timeval*)value = m_receive_timeout;
-        *value_size = sizeof(timeval);
+        copy_to_user(static_ptr_cast<timeval*>(value), &m_receive_timeout);
+        size = sizeof(timeval);
+        copy_to_user(value_size, &size);
         return KSuccess;
-    case SO_ERROR:
-        if (*value_size < sizeof(int))
+    case SO_ERROR: {
+        if (size < sizeof(int))
             return KResult(-EINVAL);
         dbg() << "getsockopt(SO_ERROR): FIXME!";
-        *(int*)value = 0;
-        *value_size = sizeof(int);
+        int errno = 0;
+        copy_to_user(static_ptr_cast<int*>(value), &errno);
+        size = sizeof(int);
+        copy_to_user(value_size, &size);
         return KSuccess;
+    }
     case SO_BINDTODEVICE:
-        if (*value_size < IFNAMSIZ)
+        if (size < IFNAMSIZ)
             return KResult(-EINVAL);
         if (m_bound_interface) {
             const auto& name = m_bound_interface->name();
             auto length = name.length() + 1;
-            memcpy(value, name.characters(), length);
-            *value_size = length;
+            copy_to_user(static_ptr_cast<char*>(value), name.characters(), length);
+            size = length;
+            copy_to_user(value_size, &size);
             return KSuccess;
         } else {
-            *value_size = 0;
+            size = 0;
+            copy_to_user(value_size, &size);
+
             return KResult(-EFAULT);
         }
     default:

+ 1 - 1
Kernel/Net/Socket.h

@@ -111,7 +111,7 @@ public:
     virtual KResultOr<size_t> recvfrom(FileDescription&, void*, size_t, int flags, sockaddr*, socklen_t*) = 0;
 
     virtual KResult setsockopt(int level, int option, Userspace<const void*>, socklen_t);
-    virtual KResult getsockopt(FileDescription&, int level, int option, void*, socklen_t*);
+    virtual KResult getsockopt(FileDescription&, int level, int option, Userspace<void*>, Userspace<socklen_t*>);
 
     pid_t origin_pid() const { return m_origin.pid; }
     uid_t origin_uid() const { return m_origin.uid; }

+ 1 - 1
Kernel/Process.h

@@ -295,7 +295,7 @@ public:
     int sys$shutdown(int sockfd, int how);
     ssize_t sys$sendto(const Syscall::SC_sendto_params*);
     ssize_t sys$recvfrom(const Syscall::SC_recvfrom_params*);
-    int sys$getsockopt(const Syscall::SC_getsockopt_params*);
+    int sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*>);
     int sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*>);
     int sys$getsockname(Userspace<const Syscall::SC_getsockname_params*>);
     int sys$getpeername(Userspace<const Syscall::SC_getpeername_params*>);

+ 14 - 12
Kernel/Syscalls/socket.cpp

@@ -316,22 +316,24 @@ int Process::sys$getpeername(Userspace<const Syscall::SC_getpeername_params*> us
     return get_sock_or_peer_name<false>(params);
 }
 
-int Process::sys$getsockopt(const Syscall::SC_getsockopt_params* params)
+int Process::sys$getsockopt(Userspace<const Syscall::SC_getsockopt_params*> user_params)
 {
-    if (!validate_read_typed(params))
+    Syscall::SC_getsockopt_params params;
+    if (!validate_read_and_copy_typed(&params, user_params))
         return -EFAULT;
 
-    SmapDisabler disabler;
-
-    int sockfd = params->sockfd;
-    int level = params->level;
-    int option = params->option;
-    void* value = params->value;
-    socklen_t* value_size = params->value_size;
+    int sockfd = params.sockfd;
+    int level = params.level;
+    int option = params.option;
+    Userspace<void*> user_value = params.value;
+    Userspace<socklen_t*> user_value_size = params.value_size;
 
-    if (!validate_write_typed(value_size))
+    if (!validate_write_typed(user_value_size))
+        return -EFAULT;
+    socklen_t value_size;
+    if (!validate_read_and_copy_typed(&value_size, user_value_size))
         return -EFAULT;
-    if (!validate_write(value, *value_size))
+    if (!validate_write(user_value, value_size))
         return -EFAULT;
     auto description = file_description(sockfd);
     if (!description)
@@ -345,7 +347,7 @@ int Process::sys$getsockopt(const Syscall::SC_getsockopt_params* params)
     } else {
         REQUIRE_PROMISE_FOR_SOCKET_DOMAIN(socket.domain());
     }
-    return socket.getsockopt(*description, level, option, value, value_size);
+    return socket.getsockopt(*description, level, option, user_value, user_value_size);
 }
 
 int Process::sys$setsockopt(Userspace<const Syscall::SC_setsockopt_params*> user_params)