]> granicus.if.org Git - strace/commitdiff
sock: rewrite decode_ifconf
authorEugene Syromyatnikov <evgsyr@gmail.com>
Fri, 23 Dec 2016 19:53:22 +0000 (22:53 +0300)
committerDmitry V. Levin <ldv@altlinux.org>
Thu, 29 Dec 2016 02:20:19 +0000 (02:20 +0000)
* sock.c (decode_ifconf): Rewrite.

Co-authored-by: Dmitry V. Levin <ldv@altlinux.org>
sock.c

diff --git a/sock.c b/sock.c
index 48f843fadc1eb5879fb10f697684d77a3d151a69..b1611f6d747ea8cee5e6e6e21951d8250d0e5222 100644 (file)
--- a/sock.c
+++ b/sock.c
@@ -131,65 +131,108 @@ print_ifc_len(int len)
        return n;
 }
 
+static bool
+print_ifconf_ifreq(struct tcb *tcp, void *elem_buf, size_t elem_size,
+                  void *dummy)
+{
+       struct ifreq *ifr = elem_buf;
+
+       tprints("{ifr_name=");
+       print_ifname(ifr->ifr_name);
+       tprints(", ");
+       PRINT_IFREQ_ADDR(tcp, ifr, ifr_addr);
+       tprints("}");
+
+       return true;
+}
+
+/*
+ * There are two different modes of operation:
+ *
+ * - Get buffer size.  In this case, the callee sets ifc_buf to NULL,
+ *   and the kernel returns the buffer size in ifc_len.
+ * - Get actual data.  In this case, the callee specifies the buffer address
+ *   in ifc_buf and its size in ifc_len.  The kernel fills the buffer with
+ *   the data, and its amount is returned in ifc_len.
+ *
+ * Note that, technically, the whole struct ifconf is overwritten,
+ * so ifc_buf could be different on exit, but current ioctl handler
+ * implementation does not touch it.
+ */
 static int
 decode_ifconf(struct tcb *const tcp, const kernel_ulong_t addr)
 {
-       struct ifconf ifc;
+       struct ifconf *entering_ifc = NULL;
+       struct ifconf *ifc =
+               entering(tcp) ? malloc(sizeof(*ifc)) : alloca(sizeof(*ifc));
 
-       if (entering(tcp)) {
-               tprints(", ");
-               if (umove_or_printaddr(tcp, addr, &ifc))
-                       return RVAL_DECODED | 1;
-               if (ifc.ifc_buf) {
-                       tprints("{");
-                       print_ifc_len(ifc.ifc_len);
+       if (exiting(tcp)) {
+               entering_ifc = get_tcb_priv_data(tcp);
+
+               if (!entering_ifc) {
+                       error_msg("decode_ifconf: where is my ifconf?");
+                       return 0;
                }
-               return 1;
        }
 
-       if (syserror(tcp) || umove(tcp, addr, &ifc) < 0) {
-               if (ifc.ifc_buf)
-                       tprints("}");
-               else
+       if (!ifc || umove(tcp, addr, ifc) < 0) {
+               if (entering(tcp)) {
+                       free(ifc);
+
+                       tprints(", ");
                        printaddr(addr);
-               return RVAL_DECODED | 1;
-       }
+               } else {
+                       /*
+                        * We failed to fetch the structure on exiting syscall,
+                        * print whatever was fetched on entering syscall.
+                        */
+                       if (!entering_ifc->ifc_buf)
+                               print_ifc_len(entering_ifc->ifc_len);
 
-       if (!ifc.ifc_buf) {
-               tprints("{");
-               print_ifc_len(ifc.ifc_len);
-               tprints(", NULL}");
-               return RVAL_DECODED | 1;
-       }
+                       tprints(", ifc_buf=");
+                       printaddr(ptr_to_kulong(entering_ifc->ifc_buf));
+
+                       tprints("}");
+               }
 
-       tprints(" => ");
-       const unsigned int nifra = print_ifc_len(ifc.ifc_len);
-       if (!nifra) {
-               tprints("}");
                return RVAL_DECODED | 1;
        }
 
-       struct ifreq ifra[nifra > max_strlen ? max_strlen : nifra];
-       tprints(", ");
-       if (umove_or_printaddr(tcp, ptr_to_kulong(ifc.ifc_buf), &ifra)) {
-               tprints("}");
-               return RVAL_DECODED | 1;
+       if (entering(tcp)) {
+               tprints(", {ifc_len=");
+               if (ifc->ifc_buf)
+                       print_ifc_len(ifc->ifc_len);
+
+               set_tcb_priv_data(tcp, ifc, free);
+
+               return 1;
        }
 
-       tprints("[");
-       unsigned int i;
-       for (i = 0; i < ARRAY_SIZE(ifra); ++i) {
-               if (i > 0)
-                       tprints(", ");
-               tprints("{ifr_name=");
-               print_ifname(ifra[i].ifr_name);
-               tprints(", ");
-               PRINT_IFREQ_ADDR(tcp, &ifra[i], ifr_addr);
-               tprints("}");
+       /* exiting */
+
+       if (entering_ifc->ifc_buf && (entering_ifc->ifc_len != ifc->ifc_len))
+               tprints(" => ");
+       if (!entering_ifc->ifc_buf || (entering_ifc->ifc_len != ifc->ifc_len))
+               print_ifc_len(ifc->ifc_len);
+
+       tprints(", ifc_buf=");
+
+       if (!entering_ifc->ifc_buf || syserror(tcp)) {
+               printaddr(ptr_to_kulong(entering_ifc->ifc_buf));
+               if (entering_ifc->ifc_buf != ifc->ifc_buf) {
+                       tprints(" => ");
+                       printaddr(ptr_to_kulong(ifc->ifc_buf));
+               }
+       } else {
+               struct ifreq ifr;
+
+               print_array(tcp, ptr_to_kulong(ifc->ifc_buf),
+                           ifc->ifc_len / sizeof(struct ifreq),
+                           &ifr, sizeof(ifr),
+                           umoven_or_printaddr, print_ifconf_ifreq, NULL);
        }
-       if (i < nifra)
-               tprints(", ...");
-       tprints("]}");
+
+       tprints("}");
 
        return RVAL_DECODED | 1;
 }