]> granicus.if.org Git - strace/blobdiff - nlattr.c
nlattr: add unsigned int decoders that print in hex form
[strace] / nlattr.c
index 8c516293fc4b974466de6ed78c3fbebb645d1ece..83a25bd6f402a0126ce58a488c2a8505c21d216c 100644 (file)
--- a/nlattr.c
+++ b/nlattr.c
@@ -1,7 +1,7 @@
 /*
  * Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
  * Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com>
- * Copyright (c) 2016-2017 The strace developers.
+ * Copyright (c) 2016-2018 The strace developers.
  * All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  */
 
 #include "defs.h"
+#include <endian.h>
 #include "netlink.h"
+#include "nlattr.h"
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <linux/sock_diag.h>
+#include "static_assert.h"
+
+#include "xlat/netlink_sk_meminfo_indices.h"
 
 static bool
 fetch_nlattr(struct tcb *const tcp, struct nlattr *const nlattr,
-            const kernel_ulong_t addr, const kernel_ulong_t len)
+            const kernel_ulong_t addr, const unsigned int len,
+            const bool in_array)
 {
        if (len < sizeof(struct nlattr)) {
-               printstrn(tcp, addr, len);
+               printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
                return false;
        }
 
-       if (umove_or_printaddr(tcp, addr, nlattr))
-               return false;
+       if (tfetch_obj(tcp, addr, nlattr))
+               return true;
 
-       return true;
+       if (in_array) {
+               tprints("...");
+               printaddr_comment(addr);
+       } else {
+               printaddr(addr);
+       }
+
+       return false;
 }
 
 static void
@@ -50,24 +66,34 @@ print_nlattr(const struct nlattr *const nla,
             const struct xlat *const table,
             const char *const dflt)
 {
+       static_assert(NLA_TYPE_MASK == ~(NLA_F_NESTED | NLA_F_NET_BYTEORDER),
+                     "wrong NLA_TYPE_MASK");
+
        tprintf("{nla_len=%u, nla_type=", nla->nla_len);
-       if (nla->nla_type & NLA_F_NESTED)
-               tprints("NLA_F_NESTED|");
-       if (nla->nla_type & NLA_F_NET_BYTEORDER)
-               tprints("NLA_F_NET_BYTEORDER|");
+       if (nla->nla_type & NLA_F_NESTED) {
+               print_xlat(NLA_F_NESTED);
+               tprints("|");
+       }
+       if (nla->nla_type & NLA_F_NET_BYTEORDER) {
+               print_xlat(NLA_F_NET_BYTEORDER);
+               tprints("|");
+       }
        printxval(table, nla->nla_type & NLA_TYPE_MASK, dflt);
        tprints("}");
 }
 
 static void
-decode_nlattr_with_data(struct tcb *tcp,
+decode_nlattr_with_data(struct tcb *const tcp,
                        const struct nlattr *const nla,
-                       kernel_ulong_t addr,
-                       kernel_ulong_t len,
+                       const kernel_ulong_t addr,
+                       const unsigned int len,
                        const struct xlat *const table,
-                       const char *const dflt)
+                       const char *const dflt,
+                       const nla_decoder_t *const decoders,
+                       const unsigned int size,
+                       const void *const opaque_data)
 {
-       const unsigned int nla_len = nla->nla_len > len ? len : nla->nla_len;
+       const unsigned int nla_len = MIN(nla->nla_len, len);
 
        if (nla_len > NLA_HDRLEN)
                tprints("{");
@@ -75,8 +101,20 @@ decode_nlattr_with_data(struct tcb *tcp,
        print_nlattr(nla, table, dflt);
 
        if (nla_len > NLA_HDRLEN) {
+               const unsigned int idx = size ? nla->nla_type : 0;
+
                tprints(", ");
-               printstrn(tcp, addr + NLA_HDRLEN, nla_len - NLA_HDRLEN);
+               if (!decoders
+                   || (size && idx >= size)
+                   || !decoders[idx]
+                   || !decoders[idx](
+                               tcp, addr + NLA_HDRLEN,
+                               nla_len - NLA_HDRLEN,
+                               size ? opaque_data
+                                    : (const void *) (uintptr_t) nla->nla_type)
+                   )
+                       printstr_ex(tcp, addr + NLA_HDRLEN,
+                                   nla_len - NLA_HDRLEN, QUOTE_FORCE_HEX);
                tprints("}");
        }
 }
@@ -84,23 +122,33 @@ decode_nlattr_with_data(struct tcb *tcp,
 void
 decode_nlattr(struct tcb *const tcp,
              kernel_ulong_t addr,
-             kernel_ulong_t len,
+             unsigned int len,
              const struct xlat *const table,
-             const char *const dflt)
+             const char *const dflt,
+             const nla_decoder_t *const decoders,
+             const unsigned int size,
+             const void *const opaque_data)
 {
        struct nlattr nla;
-       bool print_array = false;
+       bool is_array = false;
        unsigned int elt;
 
-       for (elt = 0; fetch_nlattr(tcp, &nla, addr, len); elt++) {
+       if (decoders && !size && opaque_data)
+               error_func_msg("[xlat %p, dflt \"%s\", decoders %p] "
+                              "size is zero (going to pass nla_type as "
+                              "decoder argument), but opaque data (%p) is not "
+                              "- will be ignored",
+                              table, dflt, decoders, opaque_data);
+
+       for (elt = 0; fetch_nlattr(tcp, &nla, addr, len, is_array); elt++) {
                if (abbrev(tcp) && elt == max_strlen) {
                        tprints("...");
                        break;
                }
 
-               const unsigned long nla_len = NLA_ALIGN(nla.nla_len);
+               const unsigned int nla_len = NLA_ALIGN(nla.nla_len);
                kernel_ulong_t next_addr = 0;
-               kernel_ulong_t next_len = 0;
+               unsigned int next_len = 0;
 
                if (nla.nla_len >= NLA_HDRLEN) {
                        next_len = (len >= nla_len) ? len - nla_len : 0;
@@ -109,12 +157,13 @@ decode_nlattr(struct tcb *const tcp,
                                next_addr = addr + nla_len;
                }
 
-               if (!print_array && next_addr) {
+               if (!is_array && next_addr) {
                        tprints("[");
-                       print_array = true;
+                       is_array = true;
                }
 
-               decode_nlattr_with_data(tcp, &nla, addr, len, table, dflt);
+               decode_nlattr_with_data(tcp, &nla, addr, len, table, dflt,
+                                       decoders, size, opaque_data);
 
                if (!next_addr)
                        break;
@@ -124,7 +173,256 @@ decode_nlattr(struct tcb *const tcp,
                len = next_len;
        }
 
-       if (print_array) {
+       if (is_array) {
                tprints("]");
        }
 }
+
+bool
+decode_nla_str(struct tcb *const tcp,
+              const kernel_ulong_t addr,
+              const unsigned int len,
+              const void *const opaque_data)
+{
+       printstr_ex(tcp, addr, len, QUOTE_0_TERMINATED);
+
+       return true;
+}
+
+bool
+decode_nla_strn(struct tcb *const tcp,
+               const kernel_ulong_t addr,
+               const unsigned int len,
+               const void *const opaque_data)
+{
+       printstrn(tcp, addr, len);
+
+       return true;
+}
+
+bool
+decode_nla_meminfo(struct tcb *const tcp,
+                  const kernel_ulong_t addr,
+                  const unsigned int len,
+                  const void *const opaque_data)
+{
+       uint32_t mem;
+       const size_t nmemb = len / sizeof(mem);
+
+       if (!nmemb)
+               return false;
+
+       unsigned int count = 0;
+       print_array_ex(tcp, addr, nmemb, &mem, sizeof(mem),
+                      tfetch_mem, print_uint32_array_member, &count,
+                      PAF_PRINT_INDICES | PAF_INDEX_XLAT_VALUE_INDEXED
+                       | XLAT_STYLE_FMT_U,
+                      ARRSZ_PAIR(netlink_sk_meminfo_indices),
+                      "SK_MEMINFO_???");
+
+       return true;
+}
+
+bool
+decode_nla_fd(struct tcb *const tcp,
+             const kernel_ulong_t addr,
+             const unsigned int len,
+             const void *const opaque_data)
+{
+       int fd;
+
+       if (len < sizeof(fd))
+               return false;
+       else if (!umove_or_printaddr(tcp, addr, &fd))
+               printfd(tcp, fd);
+
+       return true;
+}
+
+bool
+decode_nla_ifindex(struct tcb *const tcp,
+              const kernel_ulong_t addr,
+              const unsigned int len,
+              const void *const opaque_data)
+{
+       uint32_t ifindex;
+
+       if (len < sizeof(ifindex))
+               return false;
+       else if (!umove_or_printaddr(tcp, addr, &ifindex))
+               print_ifindex(ifindex);
+
+       return true;
+}
+
+bool
+decode_nla_xval(struct tcb *const tcp,
+               const kernel_ulong_t addr,
+               const unsigned int len,
+               const void *const opaque_data)
+{
+       const struct decode_nla_xlat_opts * const opts = opaque_data;
+       union {
+               uint64_t val;
+               uint8_t  bytes[sizeof(uint64_t)];
+       } data;
+       const size_t bytes_offs = is_bigendian ? sizeof(data) - len : 0;
+
+       data.val = 0;
+
+       if (len > sizeof(data))
+               return false;
+       else if (!umoven_or_printaddr(tcp, addr, len, data.bytes + bytes_offs))
+       {
+               if (opts->process_fn)
+                       data.val = opts->process_fn(data.val);
+               if (opts->prefix)
+                       tprints(opts->prefix);
+               printxval_dispatch_ex(opts->xlat, opts->xlat_size, data.val,
+                                     opts->dflt, opts->xt, opts->style);
+               if (opts->suffix)
+                       tprints(opts->suffix);
+       }
+
+       return true;
+}
+
+static uint64_t
+process_host_order(uint64_t val)
+{
+       return ntohs(val);
+}
+
+bool
+decode_nla_ether_proto(struct tcb *const tcp,
+                      const kernel_ulong_t addr,
+                      const unsigned int len,
+                      const void *const opaque_data)
+{
+       const struct decode_nla_xlat_opts opts = {
+               .xlat = ethernet_protocols,
+               .xlat_size = ethernet_protocols_size,
+               .dflt = "ETHER_P_???",
+               .xt = XT_SORTED,
+               .prefix = "htons(",
+               .suffix = ")",
+               .process_fn = process_host_order,
+       };
+
+       return decode_nla_xval(tcp, addr, len, &opts);
+}
+
+bool
+decode_nla_ip_proto(struct tcb *const tcp,
+                   const kernel_ulong_t addr,
+                   const unsigned int len,
+                   const void *const opaque_data)
+{
+       const struct decode_nla_xlat_opts opts = {
+               .xlat = inet_protocols,
+               .xlat_size = inet_protocols_size,
+               .xt = XT_SORTED,
+               .dflt = "IPPROTO_???",
+       };
+
+       return decode_nla_xval(tcp, addr, len, &opts);
+}
+
+bool
+decode_nla_flags(struct tcb *const tcp,
+                const kernel_ulong_t addr,
+                const unsigned int len,
+                const void *const opaque_data)
+{
+       const struct decode_nla_xlat_opts * const opts = opaque_data;
+       union {
+               uint64_t flags;
+               uint8_t  bytes[sizeof(uint64_t)];
+       } data = { .flags = 0 };
+       const size_t bytes_offs = is_bigendian ? sizeof(data) - len : 0;
+
+       if (opts->xt == XT_INDEXED)
+               error_func_msg("indexed xlats are currently incompatible with "
+                              "printflags");
+
+       if (len > sizeof(data))
+               return false;
+       else if (!umoven_or_printaddr(tcp, addr, len, data.bytes + bytes_offs))
+       {
+               if (opts->process_fn)
+                       data.flags = opts->process_fn(data.flags);
+               if (opts->prefix)
+                       tprints(opts->prefix);
+               printflags_ex(data.flags, opts->dflt, opts->style, opts->xlat,
+                             NULL);
+               if (opts->suffix)
+                       tprints(opts->suffix);
+       }
+
+       return true;
+}
+
+bool
+decode_nla_be16(struct tcb *const tcp,
+               const kernel_ulong_t addr,
+               const unsigned int len,
+               const void *const opaque_data)
+{
+       uint16_t num;
+
+       if (len < sizeof(num))
+               return false;
+       else if (!umove_or_printaddr(tcp, addr, &num))
+               tprintf("htons(%u)", ntohs(num));
+
+       return true;
+}
+
+bool
+decode_nla_be64(struct tcb *const tcp,
+               const kernel_ulong_t addr,
+               const unsigned int len,
+               const void *const opaque_data)
+{
+#if defined HAVE_BE64TOH || defined be64toh
+       uint64_t num;
+
+       if (len < sizeof(num))
+               return false;
+       else if (!umove_or_printaddr(tcp, addr, &num))
+               tprintf("htobe64(%" PRIu64 ")", be64toh(num));
+
+       return true;
+#else
+       return false;
+#endif
+}
+
+#define DECODE_NLA_INTEGER(name, type, fmt)            \
+bool                                                   \
+decode_nla_ ## name(struct tcb *const tcp,             \
+                   const kernel_ulong_t addr,          \
+                   const unsigned int len,             \
+                   const void *const opaque_data)      \
+{                                                      \
+       type num;                                       \
+                                                       \
+       if (len < sizeof(num))                          \
+               return false;                           \
+       if (!umove_or_printaddr(tcp, addr, &num))       \
+               tprintf(fmt, num);                      \
+       return true;                                    \
+}
+
+DECODE_NLA_INTEGER(x8, uint8_t, "%#" PRIx8)
+DECODE_NLA_INTEGER(x16, uint16_t, "%#" PRIx16)
+DECODE_NLA_INTEGER(x32, uint32_t, "%#" PRIx32)
+DECODE_NLA_INTEGER(x64, uint64_t, "%#" PRIx64)
+DECODE_NLA_INTEGER(u8, uint8_t, "%" PRIu8)
+DECODE_NLA_INTEGER(u16, uint16_t, "%" PRIu16)
+DECODE_NLA_INTEGER(u32, uint32_t, "%" PRIu32)
+DECODE_NLA_INTEGER(u64, uint64_t, "%" PRIu64)
+DECODE_NLA_INTEGER(s8, int8_t, "%" PRId8)
+DECODE_NLA_INTEGER(s16, int16_t, "%" PRId16)
+DECODE_NLA_INTEGER(s32, int32_t, "%" PRId32)
+DECODE_NLA_INTEGER(s64, int64_t, "%" PRId64)