]> granicus.if.org Git - strace/commitdiff
net: enhance decoding of getsockopt(PACKET_STATISTICS)
authorDmitry V. Levin <ldv@altlinux.org>
Wed, 18 Jul 2018 20:03:18 +0000 (20:03 +0000)
committerDmitry V. Levin <ldv@altlinux.org>
Wed, 18 Jul 2018 20:03:18 +0000 (20:03 +0000)
* net.c (print_tpacket_stats): Change decoder to match the kernel
behaviour: getsockopt syscall accepts any non-negative *optlen and
writes either MIN(sizeof(struct tpacket_stats), *optlen) or
MIN(sizeof(struct tpacket_stats_v3), *optlen) bytes of data.

net.c

diff --git a/net.c b/net.c
index cd1911bab19262593469479b9c00eed8915bde64..1c4e8ba22fcb22f2b8a4034196bee4b192f45b9b 100644 (file)
--- a/net.c
+++ b/net.c
@@ -651,18 +651,55 @@ print_get_ucred(struct tcb *const tcp, const kernel_ulong_t addr,
 #ifdef PACKET_STATISTICS
 static void
 print_tpacket_stats(struct tcb *const tcp, const kernel_ulong_t addr,
-                   const int len)
+                   unsigned int len)
 {
-       struct tpacket_stats stats;
+       struct tp_stats {
+               unsigned int tp_packets, tp_drops, tp_freeze_q_cnt;
+       } stats;
 
-       if (len != sizeof(stats) ||
-           umove(tcp, addr, &stats) < 0) {
-               printaddr(addr);
+       /*
+        * The kernel may return len > sizeof(stats) if the kernel structure
+        * grew as it happened when tpacket_stats_v3 was introduced.
+        */
+       if (len > sizeof(stats))
+               len = sizeof(stats);
+
+       if (umoven_or_printaddr(tcp, addr, len, &stats))
+               return;
+
+       if (len < sizeof(stats.tp_packets)) {
+               tprints("{tp_packets=");
+               print_quoted_string((void *) &stats.tp_packets,
+                                   len, QUOTE_FORCE_HEX);
        } else {
                PRINT_FIELD_U("{", stats, tp_packets);
-               PRINT_FIELD_U(", ", stats, tp_drops);
-               tprints("}");
+
+               if (len > offsetof(struct tp_stats, tp_drops)) {
+                       len -= offsetof(struct tp_stats, tp_drops);
+                       if (len < sizeof(stats.tp_drops)) {
+                               tprints(", tp_drops=");
+                               print_quoted_string((void *) &stats.tp_drops,
+                                                   len, QUOTE_FORCE_HEX);
+                       } else {
+                               PRINT_FIELD_U(", ", stats, tp_drops);
+
+                               if (len > offsetof(struct tp_stats, tp_freeze_q_cnt) -
+                                         offsetof(struct tp_stats, tp_drops)) {
+                                       len -= offsetof(struct tp_stats, tp_freeze_q_cnt) -
+                                              offsetof(struct tp_stats, tp_drops);
+                                       if (len < sizeof(stats.tp_freeze_q_cnt)) {
+                                               tprints(", tp_freeze_q_cnt=");
+                                               print_quoted_string((void *) &stats.tp_freeze_q_cnt,
+                                                                   len,
+                                                                   QUOTE_FORCE_HEX);
+                                       } else {
+                                               PRINT_FIELD_U(", ", stats, tp_freeze_q_cnt);
+                                       }
+                               }
+                       }
+               }
        }
+       tprints("}");
 }
 #endif /* PACKET_STATISTICS */