strace/nlattr.c
Dmitry V. Levin b93d52fe3d Change the license of strace to LGPL-2.1-or-later
strace is now provided under the terms of the GNU Lesser General
Public License version 2.1 or later, see COPYING for more details.

strace test suite is now provided under the terms of the GNU General
Public License version 2 or later, see tests/COPYING for more details.
2018-12-10 00:00:00 +00:00

475 lines
10 KiB
C

/*
* Copyright (c) 2016 Fabien Siron <fabien.siron@epita.fr>
* Copyright (c) 2017 JingPiao Chen <chenjingpiao@gmail.com>
* Copyright (c) 2016-2018 The strace developers.
* All rights reserved.
*
* SPDX-License-Identifier: LGPL-2.1-or-later
*/
#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 unsigned int len,
const bool in_array)
{
if (len < sizeof(struct nlattr)) {
printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX);
return false;
}
if (tfetch_obj(tcp, addr, nlattr))
return true;
if (in_array) {
tprints("...");
printaddr_comment(addr);
} else {
printaddr(addr);
}
return false;
}
static void
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) {
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 *const tcp,
const struct nlattr *const nla,
const kernel_ulong_t addr,
const unsigned int len,
const struct xlat *const table,
const char *const dflt,
const nla_decoder_t *const decoders,
const unsigned int size,
const void *const opaque_data)
{
const unsigned int nla_len = MIN(nla->nla_len, len);
if (nla_len > NLA_HDRLEN)
tprints("{");
print_nlattr(nla, table, dflt);
if (nla_len > NLA_HDRLEN) {
const unsigned int idx =
size ? nla->nla_type & NLA_TYPE_MASK : 0;
tprints(", ");
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("}");
}
}
void
decode_nlattr(struct tcb *const tcp,
kernel_ulong_t addr,
unsigned int len,
const struct xlat *const table,
const char *const dflt,
const nla_decoder_t *const decoders,
const unsigned int size,
const void *const opaque_data)
{
struct nlattr nla;
bool is_array = false;
unsigned int 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 int nla_len = NLA_ALIGN(nla.nla_len);
kernel_ulong_t next_addr = 0;
unsigned int next_len = 0;
if (nla.nla_len >= NLA_HDRLEN) {
next_len = (len >= nla_len) ? len - nla_len : 0;
if (next_len && addr + nla_len > addr)
next_addr = addr + nla_len;
}
if (!is_array && next_addr) {
tprints("[");
is_array = true;
}
decode_nlattr_with_data(tcp, &nla, addr, len, table, dflt,
decoders, size, opaque_data);
if (!next_addr)
break;
tprints(", ");
addr = next_addr;
len = next_len;
}
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_uid(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
uint32_t uid;
if (len < sizeof(uid))
return false;
else if (!umove_or_printaddr(tcp, addr, &uid))
printuid("", uid);
return true;
}
bool
decode_nla_gid(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
return decode_nla_uid(tcp, addr, len, opaque_data);
}
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,
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 = { .val = 0 };
if (len > sizeof(data) || len < opts->size)
return false;
if (opts->size)
len = MIN(len, opts->size);
const size_t bytes_offs = is_bigendian ? sizeof(data) - len : 0;
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 = ")",
.size = 2,
.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_???",
.size = 1,
};
return decode_nla_xval(tcp, addr, len, &opts);
}
bool
decode_nla_in_addr(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
struct in_addr in;
if (len < sizeof(in))
return false;
else if (!umove_or_printaddr(tcp, addr, &in))
print_inet_addr(AF_INET, &in, sizeof(in), NULL);
return true;
}
bool
decode_nla_in6_addr(struct tcb *const tcp,
const kernel_ulong_t addr,
const unsigned int len,
const void *const opaque_data)
{
struct in6_addr in6;
if (len < sizeof(in6))
return false;
else if (!umove_or_printaddr(tcp, addr, &in6))
print_inet_addr(AF_INET6, &in6, sizeof(in6), NULL);
return true;
}
bool
decode_nla_flags(struct tcb *const tcp,
const kernel_ulong_t addr,
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 };
if (len > sizeof(data) || len < opts->size)
return false;
if (opts->size)
len = MIN(len, opts->size);
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 (!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)