net: fix SOL_NETLINK NETLINK_LIST_MEMBERSHIPS decoding

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.
This commit is contained in:
Дмитрий Левин 2017-11-16 02:27:40 +00:00
parent 8d481bf4c2
commit ba41dc8a70
2 changed files with 70 additions and 21 deletions

52
net.c
View File

@ -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

View 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);