]> granicus.if.org Git - strace/commitdiff
net: enhance decoding of getsockopt(SO_PEERCRED)
authorDmitry V. Levin <ldv@altlinux.org>
Tue, 17 Jul 2018 22:07:40 +0000 (22:07 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 18 Jul 2018 20:03:18 +0000 (20:03 +0000)
* net.c (print_ucred): Rename to print_get_ucred, change decoder
to match the kernel behaviour: getsockopt syscall accepts any
non-negative *optlen and writes MIN(sizeof(struct ucred), *optlen)
bytes of data.
(print_getsockopt): Replace print_ucred with print_get_ucred.
* tests/so_percred.c: Include <string.h>.
(main): Update expected output.

net.c
tests/so_peercred.c

diff --git a/net.c b/net.c
index 1dc7dbd0735eed8b71d11c3c935d7d08e342b69e..69235aae2105e29545ee8671db9324b2f13a0652 100644 (file)
--- a/net.c
+++ b/net.c
@@ -598,30 +598,54 @@ print_get_linger(struct tcb *const tcp, const kernel_ulong_t addr,
 
 #ifdef SO_PEERCRED
 static void
-print_ucred(struct tcb *const tcp, const kernel_ulong_t addr, unsigned int len)
+print_get_ucred(struct tcb *const tcp, const kernel_ulong_t addr,
+               unsigned int len)
 {
        struct ucred uc;
 
-       if (len < sizeof(uc)) {
-               if (len != sizeof(uc.pid)
-                   && len != offsetofend(struct ucred, uid)) {
-                       printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
-                       return;
-               }
-       } else {
+       /*
+        * The kernel is very unlikely to return len > sizeof(uc)
+        * because struct ucred is very unlikely to change,
+        * but extra safety won't harm either.
+        */
+       if (len > sizeof(uc))
                len = sizeof(uc);
-       }
 
-       if (umoven(tcp, addr, len, &uc) < 0) {
-               printaddr(addr);
+       if (umoven_or_printaddr(tcp, addr, len, &uc))
                return;
-       }
 
-       PRINT_FIELD_D("{", uc, pid);
-       if (len > sizeof(uc.pid))
-               PRINT_FIELD_UID(", ", uc, uid);
-       if (len == sizeof(uc))
-               PRINT_FIELD_UID(", ", uc, gid);
+       if (len < sizeof(uc.pid)) {
+               tprints("{pid=");
+               print_quoted_string((void *) &uc.pid,
+                                   len, QUOTE_FORCE_HEX);
+       } else {
+               PRINT_FIELD_D("{", uc, pid);
+
+               if (len > offsetof(struct ucred, uid)) {
+                       len -= offsetof(struct ucred, uid);
+                       if (len < sizeof(uc.uid)) {
+                               tprints(", uid=");
+                               print_quoted_string((void *) &uc.uid,
+                                                   len, QUOTE_FORCE_HEX);
+                       } else {
+                               PRINT_FIELD_UID(", ", uc, uid);
+
+                               if (len > offsetof(struct ucred, gid) -
+                                         offsetof(struct ucred, uid)) {
+                                       len -= offsetof(struct ucred, gid) -
+                                              offsetof(struct ucred, uid);
+                                       if (len < sizeof(uc.gid)) {
+                                               tprints(", gid=");
+                                               print_quoted_string((void *) &uc.gid,
+                                                                   len,
+                                                                   QUOTE_FORCE_HEX);
+                                       } else {
+                                               PRINT_FIELD_UID(", ", uc, gid);
+                                       }
+                               }
+                       }
+               }
+       }
        tprints("}");
 }
 #endif /* SO_PEERCRED */
@@ -699,7 +723,7 @@ print_getsockopt(struct tcb *const tcp, const unsigned int level,
                        return;
 #ifdef SO_PEERCRED
                case SO_PEERCRED:
-                       print_ucred(tcp, addr, rlen);
+                       print_get_ucred(tcp, addr, rlen);
                        return;
 #endif
 #ifdef SO_ATTACH_FILTER
index 6eac8467b5c851f124ea2f9d5f2dc09bf73b7668..e4fab6d4f6b38d9b24e1842d3f24a0c7b09bdcea 100644 (file)
@@ -32,6 +32,7 @@
 
 #include <stddef.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
 
@@ -75,6 +76,25 @@ main(void)
        TAIL_ALLOC_OBJECT_CONST_PTR(struct ucred, peercred);
        TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
 
+       const unsigned int sizeof_pid = sizeof(peercred->pid);
+       struct ucred *const pid = tail_alloc(sizeof_pid);
+
+       const unsigned int sizeof_pid_truncated = sizeof_pid - 1;
+       struct ucred *const pid_truncated =
+               tail_alloc(sizeof_pid_truncated);
+
+       const unsigned int sizeof_uid = offsetofend(struct ucred, uid);
+       struct ucred *const uid = tail_alloc(sizeof_uid);
+
+       const unsigned int sizeof_uid_truncated = sizeof_uid - 1;
+       struct ucred *const uid_truncated =
+               tail_alloc(sizeof_uid_truncated);
+
+       const unsigned int sizeof_gid_truncated =
+               offsetofend(struct ucred, gid) - 1;
+       struct ucred *const gid_truncated =
+               tail_alloc(sizeof_gid_truncated);
+
        int sv[2];
        if (socketpair(AF_UNIX, SOCK_STREAM, 0, sv))
                 perror_msg_and_skip("socketpair AF_UNIX SOCK_STREAM");
@@ -88,52 +108,102 @@ main(void)
        PRINT_FIELD_UID(", ", *peercred, gid);
        printf("}, [%d]) = %s\n", *len, errstr);
 
-       int fd = socket(AF_UNIX, SOCK_STREAM, 0);
-       if (fd < 0)
-               perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
+       /* getsockopt with zero optlen */
+       *len = 0;
+       get_peercred(sv[0], peercred, len);
+       printf("getsockopt(%d, %s, %p, [0]) = %s\n",
+              sv[0], so_str(), peercred, errstr);
 
        /* getsockopt with optlen larger than necessary - shortened */
        *len = sizeof(*peercred) + 1;
-       get_peercred(fd, peercred, len);
-       printf("getsockopt(%d, %s", fd, so_str());
+       get_peercred(sv[0], peercred, len);
+       printf("getsockopt(%d, %s", sv[0], so_str());
        PRINT_FIELD_D(", {", *peercred, pid);
        PRINT_FIELD_UID(", ", *peercred, uid);
        PRINT_FIELD_UID(", ", *peercred, gid);
        printf("}, [%u->%d]) = %s\n",
               (unsigned int) sizeof(*peercred) + 1, *len, errstr);
 
-       /* getsockopt with optlen smaller than usual - truncated to ucred.pid */
-       *len = sizeof(peercred->pid);
-       get_peercred(fd, peercred, len);
-       printf("getsockopt(%d, %s", fd, so_str());
-       PRINT_FIELD_D(", {", *peercred, pid);
+       /*
+        * getsockopt with optlen less than offsetofend(struct ucred, pid):
+        * the part of struct ucred.pid is printed in hex.
+        */
+       *len = sizeof_pid_truncated;
+       get_peercred(sv[0], pid_truncated, len);
+       printf("getsockopt(%d, %s, {pid=", sv[0], so_str());
+       print_quoted_hex(pid_truncated, *len);
+       printf("}, [%d]) = %s\n", *len, errstr);
+
+       /*
+        * getsockopt with optlen equals to sizeof(struct ucred.pid):
+        * struct ucred.uid and struct ucred.gid are not printed.
+        */
+       *len = sizeof_pid;
+       get_peercred(sv[0], pid, len);
+       printf("getsockopt(%d, %s", sv[0], so_str());
+       PRINT_FIELD_D(", {", *pid, pid);
+       printf("}, [%d]) = %s\n", *len, errstr);
+
+       /*
+        * getsockopt with optlen greater than sizeof(struct ucred.pid)
+        * but smaller than offsetofend(struct ucred, uid):
+        * the part of struct ucred.uid is printed in hex.
+        */
+       *len = sizeof_uid_truncated;
+       get_peercred(sv[0], uid_truncated, len);
+       /*
+        * Copy to a properly aligned structure to avoid unaligned access
+        * to struct ucred.pid field.
+        */
+       memcpy(uid, uid_truncated, sizeof_uid_truncated);
+       printf("getsockopt(%d, %s", sv[0], so_str());
+       PRINT_FIELD_D(", {", *uid, pid);
+       printf(", uid=");
+       print_quoted_hex(&uid->uid, sizeof_uid_truncated -
+                                   offsetof(struct ucred, uid));
        printf("}, [%d]) = %s\n", *len, errstr);
 
-       /* getsockopt with optlen smaller than usual - truncated to ucred.uid */
-       *len = offsetof(struct ucred, gid);
-       get_peercred(fd, peercred, len);
-       printf("getsockopt(%d, %s", fd, so_str());
+       /*
+        * getsockopt with optlen equals to offsetofend(struct ucred, uid):
+        * struct ucred.gid is not printed.
+        */
+       *len = sizeof_uid;
+       get_peercred(sv[0], uid, len);
+       printf("getsockopt(%d, %s", sv[0], so_str());
+       PRINT_FIELD_D(", {", *uid, pid);
+       PRINT_FIELD_UID(", ", *uid, uid);
+       printf("}, [%d]) = %s\n", *len, errstr);
+
+       /*
+        * getsockopt with optlen greater than sizeof(struct ucred.uid)
+        * but smaller than offsetofend(struct ucred, gid):
+        * the part of struct ucred.gid is printed in hex.
+        */
+       *len = sizeof_gid_truncated;
+       get_peercred(sv[0], gid_truncated, len);
+       /*
+        * Copy to a properly aligned structure to avoid unaligned access
+        * to struct ucred.pid and struct ucred.uid fields.
+        */
+       memcpy(peercred, gid_truncated, sizeof_gid_truncated);
+       printf("getsockopt(%d, %s", sv[0], so_str());
        PRINT_FIELD_D(", {", *peercred, pid);
        PRINT_FIELD_UID(", ", *peercred, uid);
+       printf(", gid=");
+       print_quoted_hex(&peercred->gid, sizeof_gid_truncated -
+                                   offsetof(struct ucred, gid));
        printf("}, [%d]) = %s\n", *len, errstr);
 
-       /* getsockopt with optlen larger than usual - truncated to raw */
-       *len = sizeof(*peercred) - 1;
-       get_peercred(fd, peercred, len);
-       printf("getsockopt(%d, %s, ", fd, so_str());
-       print_quoted_hex(peercred, *len);
-       printf(", [%d]) = %s\n", *len, errstr);
-
        /* getsockopt optval EFAULT */
        *len = sizeof(*peercred);
-       get_peercred(fd, &peercred->uid, len);
+       get_peercred(sv[0], &peercred->uid, len);
        printf("getsockopt(%d, %s, %p, [%d]) = %s\n",
-              fd, so_str(), &peercred->uid, *len, errstr);
+              sv[0], so_str(), &peercred->uid, *len, errstr);
 
        /* getsockopt optlen EFAULT */
-       get_peercred(fd, peercred, len + 1);
+       get_peercred(sv[0], peercred, len + 1);
        printf("getsockopt(%d, %s, %p, %p) = %s\n",
-              fd, so_str(), peercred, len + 1, errstr);
+              sv[0], so_str(), peercred, len + 1, errstr);
 
        puts("+++ exited with 0 +++");
        return 0;