]> granicus.if.org Git - strace/commitdiff
net: fix SOL_NETLINK NETLINK_LIST_MEMBERSHIPS decoding
authorDmitry V. Levin <ldv@altlinux.org>
Thu, 16 Nov 2017 02:27:40 +0000 (02:27 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Thu, 16 Nov 2017 02:27:40 +0000 (02:27 +0000)
NETLINK_LIST_MEMBERSHIPS, unlike all other SOL_NETLINK options, requests
not just a single integer but an array of integers.  The kernel also
supports a zero optlen NETLINK_LIST_MEMBERSHIPS request.

* net.c (print_uint32): New function.
(print_getsockopt): Add ulen argument, rename len argument to rlen,
<SOL_NETLINK> Handle NETLINK_LIST_MEMBERSHIPS using print_array
and print_uint32.
(SYS_FUNC(getsockopt)): Pass ulen to print_getsockopt.
* tests/sockopt-sol_netlink.c (main): Check NETLINK_LIST_MEMBERSHIPS
decoding.

net.c
tests/sockopt-sol_netlink.c

diff --git a/net.c b/net.c
index 3e355e38864a35ca23cf78aa21897049f4d09767..498a6384a041b51464caab80209d265d2b7338b7 100644 (file)
--- a/net.c
+++ b/net.c
@@ -556,27 +556,35 @@ print_icmp_filter(struct tcb *const tcp, const kernel_ulong_t addr, int len)
        tprints(")");
 }
 
+static bool
+print_uint32(struct tcb *tcp, void *elem_buf, size_t elem_size, void *data)
+{
+       tprintf("%u", *(uint32_t *) elem_buf);
+
+       return true;
+}
+
 static void
 print_getsockopt(struct tcb *const tcp, const unsigned int level,
                 const unsigned int name, const kernel_ulong_t addr,
-                const int len)
+                const int ulen, const int rlen)
 {
        if (addr && verbose(tcp))
        switch (level) {
        case SOL_SOCKET:
                switch (name) {
                case SO_LINGER:
-                       print_get_linger(tcp, addr, len);
+                       print_get_linger(tcp, addr, rlen);
                        return;
 #ifdef SO_PEERCRED
                case SO_PEERCRED:
-                       print_ucred(tcp, addr, len);
+                       print_ucred(tcp, addr, rlen);
                        return;
 #endif
 #ifdef SO_ATTACH_FILTER
                case SO_ATTACH_FILTER:
-                       if (len && (unsigned short) len == (unsigned int) len)
-                               print_sock_fprog(tcp, addr, len);
+                       if (rlen && (unsigned short) rlen == (unsigned int) rlen)
+                               print_sock_fprog(tcp, addr, rlen);
                        else
                                printaddr(addr);
                        return;
@@ -588,7 +596,7 @@ print_getsockopt(struct tcb *const tcp, const unsigned int level,
                switch (name) {
 #ifdef PACKET_STATISTICS
                case PACKET_STATISTICS:
-                       print_tpacket_stats(tcp, addr, len);
+                       print_tpacket_stats(tcp, addr, rlen);
                        return;
 #endif
                }
@@ -597,26 +605,44 @@ print_getsockopt(struct tcb *const tcp, const unsigned int level,
        case SOL_RAW:
                switch (name) {
                case ICMP_FILTER:
-                       print_icmp_filter(tcp, addr, len);
+                       print_icmp_filter(tcp, addr, rlen);
                        return;
                }
                break;
 
        case SOL_NETLINK:
-               if (len < (int) sizeof(int))
-                       printaddr(addr);        /* unlikely */
-               else
+               if (ulen < 0 || rlen < 0) {
+                       /*
+                        * As the kernel neither accepts nor returns a negative
+                        * length, in case of successful getsockopt syscall
+                        * invocation these negative values must have come
+                        * from userspace.
+                        */
+                       printaddr(addr);
+                       return;
+               }
+               switch (name) {
+               case NETLINK_LIST_MEMBERSHIPS: {
+                       uint32_t buf;
+                       print_array(tcp, addr, MIN(ulen, rlen) / sizeof(buf),
+                                   &buf, sizeof(buf),
+                                   umoven_or_printaddr, print_uint32, 0);
+                       break;
+                       }
+               default:
                        printnum_int(tcp, addr, "%d");
+                       break;
+               }
                return;
        }
 
        /* default arg printing */
 
        if (verbose(tcp)) {
-               if (len == sizeof(int)) {
+               if (rlen == sizeof(int)) {
                        printnum_int(tcp, addr, "%d");
                } else {
-                       printstrn(tcp, addr, len);
+                       printstrn(tcp, addr, rlen);
                }
        } else {
                printaddr(addr);
@@ -649,7 +675,7 @@ SYS_FUNC(getsockopt)
                        tprintf(", [%d]", ulen);
                } else {
                        print_getsockopt(tcp, tcp->u_arg[1], tcp->u_arg[2],
-                                        tcp->u_arg[3], rlen);
+                                        tcp->u_arg[3], ulen, rlen);
                        if (ulen != rlen)
                                tprintf(", [%d->%d]", ulen, rlen);
                        else
index b17f9333edc4415ea40274d7e831cb24d78c6fc7..066920c634f112e05c549d14941e173ea175d96e 100644 (file)
@@ -85,6 +85,9 @@ main(void)
 #ifdef NETLINK_LISTEN_ALL_NSID
                { ARG_STR(NETLINK_LISTEN_ALL_NSID) },
 #endif
+#ifdef NETLINK_LIST_MEMBERSHIPS
+               { ARG_STR(NETLINK_LIST_MEMBERSHIPS) },
+#endif
 #ifdef NETLINK_CAP_ACK
                { ARG_STR(NETLINK_CAP_ACK) },
 #endif
@@ -136,14 +139,34 @@ main(void)
                        printf("->%d", *len);
                printf("]) = %s\n", errstr);
 
-               /* optlen shorter than necessary - print address */
-               *len = sizeof(*val) - 1;
-               get_sockopt(fd, names[i].val, val, len);
-               printf("getsockopt(%d, SOL_NETLINK, %s, %p, [%d",
-                      fd, names[i].str, val, (int) sizeof(*val) - 1);
-               if ((int) sizeof(*val) - 1 != *len)
-                       printf("->%d", *len);
-               printf("]) = %s\n", errstr);
+#ifdef NETLINK_LIST_MEMBERSHIPS
+               if (names[i].val != NETLINK_LIST_MEMBERSHIPS) {
+#endif
+                       /* optlen shorter than necessary - print address */
+                       *len = sizeof(*val) - 1;
+                       get_sockopt(fd, names[i].val, val, len);
+                       printf("getsockopt(%d, SOL_NETLINK, %s, %p, [%d",
+                              fd, names[i].str, val, (int) sizeof(*val) - 1);
+                       if ((int) sizeof(*val) - 1 != *len)
+                               printf("->%d", *len);
+                       printf("]) = %s\n", errstr);
+#ifdef NETLINK_LIST_MEMBERSHIPS
+               } else {
+                       /* optlen shorter than required for the first element */
+                       *len = sizeof(*val) - 1;
+                       get_sockopt(fd, names[i].val, efault, len);
+                       printf("getsockopt(%d, SOL_NETLINK, %s, ",
+                              fd, names[i].str);
+                       if (rc)
+                               printf("%p", efault);
+                       else
+                               printf("[]");
+                       printf(", [%d", (int) sizeof(*val) - 1);
+                       if ((int) sizeof(*val) - 1 != *len)
+                               printf("->%d", *len);
+                       printf("]) = %s\n", errstr);
+               }
+#endif
 
                /* optval EFAULT - print address */
                *len = sizeof(*val);