]> granicus.if.org Git - strace/commitdiff
net: enhance decoding of getsockopt(SO_LINGER)
authorDmitry V. Levin <ldv@altlinux.org>
Mon, 16 Jul 2018 22:57:59 +0000 (22:57 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 18 Jul 2018 19:25:52 +0000 (19:25 +0000)
* net.c (print_get_linger): Change decoder to match the kernel
behaviour: getsockopt syscall accepts any non-negative *optlen
and writes MIN(sizeof(struct linger), *optlen) bytes of data.
(print_set_linger): Move after definition of SYS_FUNC(getsockopt).
* tests/so_linger.c: Include <stddef.h> and <string.h>.
(main): Update expected output.

net.c
tests/so_linger.c

diff --git a/net.c b/net.c
index f35960af76b15473a93ee0df6cbfa07e557067a5..1dc7dbd0735eed8b71d11c3c935d7d08e342b69e 100644 (file)
--- a/net.c
+++ b/net.c
@@ -560,44 +560,39 @@ print_sockopt_fd_level_name(struct tcb *tcp, int fd, unsigned int level,
        tprints(", ");
 }
 
-static void
-print_set_linger(struct tcb *const tcp, const kernel_ulong_t addr,
-                const int len)
-{
-       struct linger linger;
-
-       if (len < (int) sizeof(linger)) {
-               printaddr(addr);
-       } else if (!umove_or_printaddr(tcp, addr, &linger)) {
-               PRINT_FIELD_D("{", linger, l_onoff);
-               PRINT_FIELD_D(", ", linger, l_linger);
-               tprints("}");
-       }
-}
-
 static void
 print_get_linger(struct tcb *const tcp, const kernel_ulong_t addr,
                 unsigned int len)
 {
        struct linger linger;
 
-       if (len < sizeof(linger)) {
-               if (len != sizeof(linger.l_onoff)) {
-                       printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
-                       return;
-               }
-       } else {
+       /*
+        * The kernel cannot return len > sizeof(linger) because struct linger
+        * cannot change, but extra safety won't harm either.
+        */
+       if (len > sizeof(linger))
                len = sizeof(linger);
-       }
-
-       if (umoven(tcp, addr, len, &linger) < 0) {
-               printaddr(addr);
+       if (umoven_or_printaddr(tcp, addr, len, &linger))
                return;
-       }
 
-       PRINT_FIELD_D("{", linger, l_onoff);
-       if (len == sizeof(linger))
-               PRINT_FIELD_D(", ", linger, l_linger);
+       if (len < sizeof(linger.l_onoff)) {
+               tprints("{l_onoff=");
+               print_quoted_string((void *) &linger.l_onoff,
+                                   len, QUOTE_FORCE_HEX);
+       } else {
+               PRINT_FIELD_D("{", linger, l_onoff);
+
+               if (len > offsetof(struct linger, l_linger)) {
+                       len -= offsetof(struct linger, l_linger);
+                       if (len < sizeof(linger.l_linger)) {
+                               tprints(", l_linger=");
+                               print_quoted_string((void *) &linger.l_linger,
+                                                   len, QUOTE_FORCE_HEX);
+                       } else {
+                               PRINT_FIELD_D(", ", linger, l_linger);
+                       }
+               }
+       }
        tprints("}");
 }
 
@@ -807,6 +802,21 @@ SYS_FUNC(getsockopt)
        return 0;
 }
 
+static void
+print_set_linger(struct tcb *const tcp, const kernel_ulong_t addr,
+                const int len)
+{
+       struct linger linger;
+
+       if (len < (int) sizeof(linger)) {
+               printaddr(addr);
+       } else if (!umove_or_printaddr(tcp, addr, &linger)) {
+               PRINT_FIELD_D("{", linger, l_onoff);
+               PRINT_FIELD_D(", ", linger, l_linger);
+               tprints("}");
+       }
+}
+
 #ifdef IP_ADD_MEMBERSHIP
 static void
 print_mreq(struct tcb *const tcp, const kernel_ulong_t addr,
index 4dc994bf6268b2b0d9cb8f8be9f02fe9c16fdf2a..44feac7e49e99f999cbfa61677f86cee5809d570 100644 (file)
@@ -29,7 +29,9 @@
 
 #include "tests.h"
 
+#include <stddef.h>
 #include <stdio.h>
+#include <string.h>
 #include <sys/socket.h>
 #include <unistd.h>
 
@@ -56,6 +58,19 @@ main(void)
 {
        TAIL_ALLOC_OBJECT_CONST_PTR(struct linger, linger);
        TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len);
+
+       const unsigned int sizeof_l_onoff = sizeof(linger->l_onoff);
+       struct linger *const l_onoff = tail_alloc(sizeof_l_onoff);
+
+       const unsigned int sizeof_l_onoff_truncated = sizeof_l_onoff - 1;
+       struct linger *const l_onoff_truncated =
+               tail_alloc(sizeof_l_onoff_truncated);
+
+       const unsigned int sizeof_l_linger_truncated =
+               offsetofend(struct linger, l_linger) - 1;
+       struct linger *const l_linger_truncated =
+               tail_alloc(sizeof_l_linger_truncated);
+
         int fd = socket(AF_UNIX, SOCK_STREAM, 0);
         if (fd < 0)
                 perror_msg_and_skip("socket AF_UNIX SOCK_STREAM");
@@ -98,6 +113,12 @@ main(void)
        printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n",
               fd, &linger->l_linger, (unsigned int) sizeof(*linger), errstr);
 
+       /* getsockopt with zero optlen */
+       *len = 0;
+       get_linger(fd, linger, len);
+       printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [0]) = %s\n",
+              fd, linger, errstr);
+
        /* getsockopt with optlen larger than necessary - shortened */
        *len = sizeof(*linger) + 1;
        get_linger(fd, linger, len);
@@ -106,19 +127,43 @@ main(void)
               fd, linger->l_onoff, linger->l_linger,
               (unsigned int) sizeof(*linger) + 1, *len, errstr);
 
-       /* getsockopt with optlen larger than usual - truncated to l_onoff */
-       *len = sizeof(linger->l_onoff);
-       get_linger(fd, linger, len);
+       /*
+        * getsockopt with optlen less than sizeof(linger->l_onoff):
+        * the part of struct linger.l_onoff is printed in hex.
+        */
+       *len = sizeof_l_onoff_truncated;
+       get_linger(fd, l_onoff_truncated, len);
+       printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=", fd);
+       print_quoted_hex(l_onoff_truncated, *len);
+       printf("}, [%d]) = %s\n", *len, errstr);
+
+       /*
+        * getsockopt with optlen equals to sizeof(struct linger.l_onoff):
+        * struct linger.l_linger is not printed.
+        */
+       *len = sizeof_l_onoff;
+       get_linger(fd, l_onoff, len);
        printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d}"
               ", [%d]) = %s\n",
-              fd, linger->l_onoff, *len, errstr);
-
-       /* getsockopt with optlen larger than usual - truncated to raw */
-       *len = sizeof(*linger) - 1;
-       get_linger(fd, linger, len);
-       printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, ", fd);
-       print_quoted_hex(linger, *len);
-       printf(", [%d]) = %s\n", *len, errstr);
+              fd, l_onoff->l_onoff, *len, errstr);
+
+       /*
+        * getsockopt with optlen greater than sizeof(struct linger.l_onoff)
+        * but smaller than sizeof(struct linger):
+        * the part of struct linger.l_linger is printed in hex.
+        */
+       *len = sizeof_l_linger_truncated;
+       get_linger(fd, l_linger_truncated, len);
+       /*
+        * Copy to a properly aligned structure to avoid unaligned access
+        * to struct linger.l_onoff field.
+        */
+       memcpy(linger, l_linger_truncated, sizeof_l_linger_truncated);
+       printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=",
+              fd, linger->l_onoff);
+       print_quoted_hex(&linger->l_linger, sizeof_l_linger_truncated -
+                                           offsetof(struct linger, l_linger));
+       printf("}, [%d]) = %s\n", *len, errstr);
 
        /* getsockopt optval EFAULT */
        *len = sizeof(*linger);