From: Dmitry V. Levin Date: Mon, 16 Jul 2018 22:57:59 +0000 (+0000) Subject: net: enhance decoding of getsockopt(SO_LINGER) X-Git-Tag: v4.24~37 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=9ccd194f1dff79c3baed2af2cb8d8367f69b99f4;p=strace net: enhance decoding of getsockopt(SO_LINGER) * net.c (print_get_linger): Change decoder to match the kernel behaviour: getsockopt syscall accepts any non-negative *optlen and writes MIN(sizeof(struct linger), *optlen) bytes of data. (print_set_linger): Move after definition of SYS_FUNC(getsockopt). * tests/so_linger.c: Include and . (main): Update expected output. --- diff --git a/net.c b/net.c index f35960af..1dc7dbd0 100644 --- a/net.c +++ b/net.c @@ -560,44 +560,39 @@ print_sockopt_fd_level_name(struct tcb *tcp, int fd, unsigned int level, tprints(", "); } -static void -print_set_linger(struct tcb *const tcp, const kernel_ulong_t addr, - const int len) -{ - struct linger linger; - - if (len < (int) sizeof(linger)) { - printaddr(addr); - } else if (!umove_or_printaddr(tcp, addr, &linger)) { - PRINT_FIELD_D("{", linger, l_onoff); - PRINT_FIELD_D(", ", linger, l_linger); - tprints("}"); - } -} - static void print_get_linger(struct tcb *const tcp, const kernel_ulong_t addr, unsigned int len) { struct linger linger; - if (len < sizeof(linger)) { - if (len != sizeof(linger.l_onoff)) { - printstr_ex(tcp, addr, len, QUOTE_FORCE_HEX); - return; - } - } else { + /* + * The kernel cannot return len > sizeof(linger) because struct linger + * cannot change, but extra safety won't harm either. + */ + if (len > sizeof(linger)) len = sizeof(linger); - } - - if (umoven(tcp, addr, len, &linger) < 0) { - printaddr(addr); + if (umoven_or_printaddr(tcp, addr, len, &linger)) return; - } - PRINT_FIELD_D("{", linger, l_onoff); - if (len == sizeof(linger)) - PRINT_FIELD_D(", ", linger, l_linger); + if (len < sizeof(linger.l_onoff)) { + tprints("{l_onoff="); + print_quoted_string((void *) &linger.l_onoff, + len, QUOTE_FORCE_HEX); + } else { + PRINT_FIELD_D("{", linger, l_onoff); + + if (len > offsetof(struct linger, l_linger)) { + len -= offsetof(struct linger, l_linger); + if (len < sizeof(linger.l_linger)) { + tprints(", l_linger="); + print_quoted_string((void *) &linger.l_linger, + len, QUOTE_FORCE_HEX); + } else { + PRINT_FIELD_D(", ", linger, l_linger); + } + } + } tprints("}"); } @@ -807,6 +802,21 @@ SYS_FUNC(getsockopt) return 0; } +static void +print_set_linger(struct tcb *const tcp, const kernel_ulong_t addr, + const int len) +{ + struct linger linger; + + if (len < (int) sizeof(linger)) { + printaddr(addr); + } else if (!umove_or_printaddr(tcp, addr, &linger)) { + PRINT_FIELD_D("{", linger, l_onoff); + PRINT_FIELD_D(", ", linger, l_linger); + tprints("}"); + } +} + #ifdef IP_ADD_MEMBERSHIP static void print_mreq(struct tcb *const tcp, const kernel_ulong_t addr, diff --git a/tests/so_linger.c b/tests/so_linger.c index 4dc994bf..44feac7e 100644 --- a/tests/so_linger.c +++ b/tests/so_linger.c @@ -29,7 +29,9 @@ #include "tests.h" +#include #include +#include #include #include @@ -56,6 +58,19 @@ main(void) { TAIL_ALLOC_OBJECT_CONST_PTR(struct linger, linger); TAIL_ALLOC_OBJECT_CONST_PTR(socklen_t, len); + + const unsigned int sizeof_l_onoff = sizeof(linger->l_onoff); + struct linger *const l_onoff = tail_alloc(sizeof_l_onoff); + + const unsigned int sizeof_l_onoff_truncated = sizeof_l_onoff - 1; + struct linger *const l_onoff_truncated = + tail_alloc(sizeof_l_onoff_truncated); + + const unsigned int sizeof_l_linger_truncated = + offsetofend(struct linger, l_linger) - 1; + struct linger *const l_linger_truncated = + tail_alloc(sizeof_l_linger_truncated); + int fd = socket(AF_UNIX, SOCK_STREAM, 0); if (fd < 0) perror_msg_and_skip("socket AF_UNIX SOCK_STREAM"); @@ -98,6 +113,12 @@ main(void) printf("setsockopt(%d, SOL_SOCKET, SO_LINGER, %p, %d) = %s\n", fd, &linger->l_linger, (unsigned int) sizeof(*linger), errstr); + /* getsockopt with zero optlen */ + *len = 0; + get_linger(fd, linger, len); + printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, %p, [0]) = %s\n", + fd, linger, errstr); + /* getsockopt with optlen larger than necessary - shortened */ *len = sizeof(*linger) + 1; get_linger(fd, linger, len); @@ -106,19 +127,43 @@ main(void) fd, linger->l_onoff, linger->l_linger, (unsigned int) sizeof(*linger) + 1, *len, errstr); - /* getsockopt with optlen larger than usual - truncated to l_onoff */ - *len = sizeof(linger->l_onoff); - get_linger(fd, linger, len); + /* + * getsockopt with optlen less than sizeof(linger->l_onoff): + * the part of struct linger.l_onoff is printed in hex. + */ + *len = sizeof_l_onoff_truncated; + get_linger(fd, l_onoff_truncated, len); + printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=", fd); + print_quoted_hex(l_onoff_truncated, *len); + printf("}, [%d]) = %s\n", *len, errstr); + + /* + * getsockopt with optlen equals to sizeof(struct linger.l_onoff): + * struct linger.l_linger is not printed. + */ + *len = sizeof_l_onoff; + get_linger(fd, l_onoff, len); printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d}" ", [%d]) = %s\n", - fd, linger->l_onoff, *len, errstr); - - /* getsockopt with optlen larger than usual - truncated to raw */ - *len = sizeof(*linger) - 1; - get_linger(fd, linger, len); - printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, ", fd); - print_quoted_hex(linger, *len); - printf(", [%d]) = %s\n", *len, errstr); + fd, l_onoff->l_onoff, *len, errstr); + + /* + * getsockopt with optlen greater than sizeof(struct linger.l_onoff) + * but smaller than sizeof(struct linger): + * the part of struct linger.l_linger is printed in hex. + */ + *len = sizeof_l_linger_truncated; + get_linger(fd, l_linger_truncated, len); + /* + * Copy to a properly aligned structure to avoid unaligned access + * to struct linger.l_onoff field. + */ + memcpy(linger, l_linger_truncated, sizeof_l_linger_truncated); + printf("getsockopt(%d, SOL_SOCKET, SO_LINGER, {l_onoff=%d, l_linger=", + fd, linger->l_onoff); + print_quoted_hex(&linger->l_linger, sizeof_l_linger_truncated - + offsetof(struct linger, l_linger)); + printf("}, [%d]) = %s\n", *len, errstr); /* getsockopt optval EFAULT */ *len = sizeof(*linger);