Message ID | 20191106130456.6176-2-laurent@vivier.eu |
---|---|
State | New |
Headers | show |
Series | [PULL,v2,01/13] linux-user: Support for NETLINK socket options | expand |
On Wed, 6 Nov 2019 at 13:07, Laurent Vivier <laurent@vivier.eu> wrote: > > From: Josh Kunz <jkz@google.com> > > This change includes support for all AF_NETLINK socket options up to about > kernel version 5.4 (5.4 is not formally released at the time of writing). > Socket options that were introduced in kernel versions before the oldest > currently stable kernel version are guarded by kernel version macros. > > This change has been built under gcc 8.3, and clang 9.0, and it passes > `make check`. The netlink options have been tested by emulating some > non-trival software that uses NETLINK socket options, but they have > not been exaustively verified. Hi; Coverity reports a missing-break-in-switch error for this commit (CID 1407221): > diff --git a/linux-user/syscall.c b/linux-user/syscall.c > index f6751eecb78c..247883292ce5 100644 > --- a/linux-user/syscall.c > +++ b/linux-user/syscall.c > @@ -2248,6 +2248,39 @@ set_timeout: > return -TARGET_EFAULT; > ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val))); > break; > +#ifdef SOL_NETLINK > + case SOL_NETLINK: > + switch (optname) { > + case NETLINK_PKTINFO: > + case NETLINK_ADD_MEMBERSHIP: > + case NETLINK_DROP_MEMBERSHIP: > + case NETLINK_BROADCAST_ERROR: > + case NETLINK_NO_ENOBUFS: > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) > + case NETLINK_LISTEN_ALL_NSID: > + case NETLINK_CAP_ACK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) > + case NETLINK_EXT_ACK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) > + case NETLINK_GET_STRICT_CHK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ > + break; > + default: > + goto unimplemented; > + } > + val = 0; > + if (optlen < sizeof(uint32_t)) { > + return -TARGET_EINVAL; > + } > + if (get_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + ret = get_errno(setsockopt(sockfd, SOL_NETLINK, optname, &val, > + sizeof(val))); > + break; > +#endif /* SOL_NETLINK */ > default: > unimplemented: > gemu_log("Unsupported setsockopt level=%d optname=%d\n", level, optname); > @@ -2532,6 +2565,74 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, > break; > } > break; > +#ifdef SOL_NETLINK > + case SOL_NETLINK: > + switch (optname) { > + case NETLINK_PKTINFO: > + case NETLINK_BROADCAST_ERROR: > + case NETLINK_NO_ENOBUFS: > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) > + case NETLINK_LISTEN_ALL_NSID: > + case NETLINK_CAP_ACK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) > + case NETLINK_EXT_ACK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) > + case NETLINK_GET_STRICT_CHK: > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ > + if (get_user_u32(len, optlen)) { > + return -TARGET_EFAULT; > + } > + if (len != sizeof(val)) { > + return -TARGET_EINVAL; > + } > + lv = len; > + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); > + if (ret < 0) { > + return ret; > + } > + if (put_user_u32(lv, optlen) > + || put_user_u32(val, optval_addr)) { > + return -TARGET_EFAULT; > + } > + break; > +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) > + case NETLINK_LIST_MEMBERSHIPS: > + { > + uint32_t *results; > + int i; > + if (get_user_u32(len, optlen)) { > + return -TARGET_EFAULT; > + } > + if (len < 0) { > + return -TARGET_EINVAL; > + } > + results = lock_user(VERIFY_WRITE, optval_addr, len, 1); > + if (!results) { > + return -TARGET_EFAULT; > + } > + lv = len; > + ret = get_errno(getsockopt(sockfd, level, optname, results, &lv)); > + if (ret < 0) { > + unlock_user(results, optval_addr, 0); > + return ret; > + } > + /* swap host endianess to target endianess. */ > + for (i = 0; i < (len / sizeof(uint32_t)); i++) { > + results[i] = tswap32(results[i]); > + } > + if (put_user_u32(lv, optlen)) { > + return -TARGET_EFAULT; > + } > + unlock_user(results, optval_addr, 0); > + break; > + } > +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ > + default: > + goto unimplemented; > + } > +#endif /* SOL_NETLINK */ Here at the end of the 'case SOL_NETLINK' we will just fall straight through into 'default:'. Missing 'break' ? > default: > unimplemented: > gemu_log("getsockopt level=%d optname=%d not yet supported\n", > -- > 2.21.0 thanks -- PMM
Le 12/11/2019 à 11:11, Peter Maydell a écrit : > On Wed, 6 Nov 2019 at 13:07, Laurent Vivier <laurent@vivier.eu> wrote: >> >> From: Josh Kunz <jkz@google.com> >> >> This change includes support for all AF_NETLINK socket options up to about >> kernel version 5.4 (5.4 is not formally released at the time of writing). >> Socket options that were introduced in kernel versions before the oldest >> currently stable kernel version are guarded by kernel version macros. >> >> This change has been built under gcc 8.3, and clang 9.0, and it passes >> `make check`. The netlink options have been tested by emulating some >> non-trival software that uses NETLINK socket options, but they have >> not been exaustively verified. > > Hi; Coverity reports a missing-break-in-switch error for > this commit (CID 1407221): > >> diff --git a/linux-user/syscall.c b/linux-user/syscall.c >> index f6751eecb78c..247883292ce5 100644 >> --- a/linux-user/syscall.c >> +++ b/linux-user/syscall.c >> @@ -2248,6 +2248,39 @@ set_timeout: >> return -TARGET_EFAULT; >> ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val))); >> break; >> +#ifdef SOL_NETLINK >> + case SOL_NETLINK: >> + switch (optname) { >> + case NETLINK_PKTINFO: >> + case NETLINK_ADD_MEMBERSHIP: >> + case NETLINK_DROP_MEMBERSHIP: >> + case NETLINK_BROADCAST_ERROR: >> + case NETLINK_NO_ENOBUFS: >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) >> + case NETLINK_LISTEN_ALL_NSID: >> + case NETLINK_CAP_ACK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) >> + case NETLINK_EXT_ACK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) >> + case NETLINK_GET_STRICT_CHK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ >> + break; >> + default: >> + goto unimplemented; >> + } >> + val = 0; >> + if (optlen < sizeof(uint32_t)) { >> + return -TARGET_EINVAL; >> + } >> + if (get_user_u32(val, optval_addr)) { >> + return -TARGET_EFAULT; >> + } >> + ret = get_errno(setsockopt(sockfd, SOL_NETLINK, optname, &val, >> + sizeof(val))); >> + break; >> +#endif /* SOL_NETLINK */ >> default: >> unimplemented: >> gemu_log("Unsupported setsockopt level=%d optname=%d\n", level, optname); >> @@ -2532,6 +2565,74 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, >> break; >> } >> break; >> +#ifdef SOL_NETLINK >> + case SOL_NETLINK: >> + switch (optname) { >> + case NETLINK_PKTINFO: >> + case NETLINK_BROADCAST_ERROR: >> + case NETLINK_NO_ENOBUFS: >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) >> + case NETLINK_LISTEN_ALL_NSID: >> + case NETLINK_CAP_ACK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) >> + case NETLINK_EXT_ACK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) >> + case NETLINK_GET_STRICT_CHK: >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ >> + if (get_user_u32(len, optlen)) { >> + return -TARGET_EFAULT; >> + } >> + if (len != sizeof(val)) { >> + return -TARGET_EINVAL; >> + } >> + lv = len; >> + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); >> + if (ret < 0) { >> + return ret; >> + } >> + if (put_user_u32(lv, optlen) >> + || put_user_u32(val, optval_addr)) { >> + return -TARGET_EFAULT; >> + } >> + break; >> +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) >> + case NETLINK_LIST_MEMBERSHIPS: >> + { >> + uint32_t *results; >> + int i; >> + if (get_user_u32(len, optlen)) { >> + return -TARGET_EFAULT; >> + } >> + if (len < 0) { >> + return -TARGET_EINVAL; >> + } >> + results = lock_user(VERIFY_WRITE, optval_addr, len, 1); >> + if (!results) { >> + return -TARGET_EFAULT; >> + } >> + lv = len; >> + ret = get_errno(getsockopt(sockfd, level, optname, results, &lv)); >> + if (ret < 0) { >> + unlock_user(results, optval_addr, 0); >> + return ret; >> + } >> + /* swap host endianess to target endianess. */ >> + for (i = 0; i < (len / sizeof(uint32_t)); i++) { >> + results[i] = tswap32(results[i]); >> + } >> + if (put_user_u32(lv, optlen)) { >> + return -TARGET_EFAULT; >> + } >> + unlock_user(results, optval_addr, 0); >> + break; >> + } >> +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ >> + default: >> + goto unimplemented; >> + } >> +#endif /* SOL_NETLINK */ > > Here at the end of the 'case SOL_NETLINK' we will just > fall straight through into 'default:'. Missing 'break' ? Yes, missing 'break'. I'm going to send a patch to fix that. Thanks, Laurent
diff --git a/linux-user/syscall.c b/linux-user/syscall.c index f6751eecb78c..247883292ce5 100644 --- a/linux-user/syscall.c +++ b/linux-user/syscall.c @@ -2248,6 +2248,39 @@ set_timeout: return -TARGET_EFAULT; ret = get_errno(setsockopt(sockfd, SOL_SOCKET, optname, &val, sizeof(val))); break; +#ifdef SOL_NETLINK + case SOL_NETLINK: + switch (optname) { + case NETLINK_PKTINFO: + case NETLINK_ADD_MEMBERSHIP: + case NETLINK_DROP_MEMBERSHIP: + case NETLINK_BROADCAST_ERROR: + case NETLINK_NO_ENOBUFS: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + case NETLINK_LISTEN_ALL_NSID: + case NETLINK_CAP_ACK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + case NETLINK_EXT_ACK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + case NETLINK_GET_STRICT_CHK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ + break; + default: + goto unimplemented; + } + val = 0; + if (optlen < sizeof(uint32_t)) { + return -TARGET_EINVAL; + } + if (get_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + ret = get_errno(setsockopt(sockfd, SOL_NETLINK, optname, &val, + sizeof(val))); + break; +#endif /* SOL_NETLINK */ default: unimplemented: gemu_log("Unsupported setsockopt level=%d optname=%d\n", level, optname); @@ -2532,6 +2565,74 @@ static abi_long do_getsockopt(int sockfd, int level, int optname, break; } break; +#ifdef SOL_NETLINK + case SOL_NETLINK: + switch (optname) { + case NETLINK_PKTINFO: + case NETLINK_BROADCAST_ERROR: + case NETLINK_NO_ENOBUFS: +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + case NETLINK_LISTEN_ALL_NSID: + case NETLINK_CAP_ACK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) + case NETLINK_EXT_ACK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 20, 0) + case NETLINK_GET_STRICT_CHK: +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 12, 0) */ + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len != sizeof(val)) { + return -TARGET_EINVAL; + } + lv = len; + ret = get_errno(getsockopt(sockfd, level, optname, &val, &lv)); + if (ret < 0) { + return ret; + } + if (put_user_u32(lv, optlen) + || put_user_u32(val, optval_addr)) { + return -TARGET_EFAULT; + } + break; +#if LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) + case NETLINK_LIST_MEMBERSHIPS: + { + uint32_t *results; + int i; + if (get_user_u32(len, optlen)) { + return -TARGET_EFAULT; + } + if (len < 0) { + return -TARGET_EINVAL; + } + results = lock_user(VERIFY_WRITE, optval_addr, len, 1); + if (!results) { + return -TARGET_EFAULT; + } + lv = len; + ret = get_errno(getsockopt(sockfd, level, optname, results, &lv)); + if (ret < 0) { + unlock_user(results, optval_addr, 0); + return ret; + } + /* swap host endianess to target endianess. */ + for (i = 0; i < (len / sizeof(uint32_t)); i++) { + results[i] = tswap32(results[i]); + } + if (put_user_u32(lv, optlen)) { + return -TARGET_EFAULT; + } + unlock_user(results, optval_addr, 0); + break; + } +#endif /* LINUX_VERSION_CODE >= KERNEL_VERSION(4, 2, 0) */ + default: + goto unimplemented; + } +#endif /* SOL_NETLINK */ default: unimplemented: gemu_log("getsockopt level=%d optname=%d not yet supported\n",